aboutsummaryrefslogtreecommitdiffstats
path: root/sound/drivers/pcsp
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2008-11-26 08:12:42 -0500
committerTakashi Iwai <tiwai@suse.de>2008-11-26 08:12:42 -0500
commite7dd8c1bdacf658b0ade51facb2f7eaf40eb0ac4 (patch)
tree0db15820fdd391a1570e1257167e8f02532c83f5 /sound/drivers/pcsp
parented313489badef16d700f5a3be50e8fd8f8294bc8 (diff)
parentbc4a68fed4b4c01005ef3c71ede6a8cbe91b7dc9 (diff)
Merge branch 'topic/misc' into topic/pcsp-fix
Conflicts: sound/drivers/pcsp/pcsp_lib.c
Diffstat (limited to 'sound/drivers/pcsp')
-rw-r--r--sound/drivers/pcsp/pcsp.c8
-rw-r--r--sound/drivers/pcsp/pcsp.h1
-rw-r--r--sound/drivers/pcsp/pcsp_lib.c95
3 files changed, 56 insertions, 48 deletions
diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c
index 1899cf0685bc..2a02f704f366 100644
--- a/sound/drivers/pcsp/pcsp.c
+++ b/sound/drivers/pcsp/pcsp.c
@@ -96,7 +96,7 @@ static int __devinit snd_card_pcsp_probe(int devnum, struct device *dev)
96 return -EINVAL; 96 return -EINVAL;
97 97
98 hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 98 hrtimer_init(&pcsp_chip.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
99 pcsp_chip.timer.cb_mode = HRTIMER_CB_SOFTIRQ; 99 pcsp_chip.timer.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED;
100 pcsp_chip.timer.function = pcsp_do_timer; 100 pcsp_chip.timer.function = pcsp_do_timer;
101 101
102 card = snd_card_new(index, id, THIS_MODULE, 0); 102 card = snd_card_new(index, id, THIS_MODULE, 0);
@@ -188,10 +188,8 @@ static int __devexit pcsp_remove(struct platform_device *dev)
188 188
189static void pcsp_stop_beep(struct snd_pcsp *chip) 189static void pcsp_stop_beep(struct snd_pcsp *chip)
190{ 190{
191 spin_lock_irq(&chip->substream_lock); 191 pcsp_sync_stop(chip);
192 if (!chip->playback_substream) 192 pcspkr_stop_sound();
193 pcspkr_stop_sound();
194 spin_unlock_irq(&chip->substream_lock);
195} 193}
196 194
197#ifdef CONFIG_PM 195#ifdef CONFIG_PM
diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h
index 1d661f795e8c..70533a333b5b 100644
--- a/sound/drivers/pcsp/pcsp.h
+++ b/sound/drivers/pcsp/pcsp.h
@@ -77,6 +77,7 @@ struct snd_pcsp {
77extern struct snd_pcsp pcsp_chip; 77extern struct snd_pcsp pcsp_chip;
78 78
79extern enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle); 79extern enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle);
80extern void pcsp_sync_stop(struct snd_pcsp *chip);
80 81
81extern int snd_pcsp_new_pcm(struct snd_pcsp *chip); 82extern int snd_pcsp_new_pcm(struct snd_pcsp *chip);
82extern int snd_pcsp_new_mixer(struct snd_pcsp *chip); 83extern int snd_pcsp_new_mixer(struct snd_pcsp *chip);
diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c
index 1f42e4063118..f8d8470861da 100644
--- a/sound/drivers/pcsp/pcsp_lib.c
+++ b/sound/drivers/pcsp/pcsp_lib.c
@@ -8,6 +8,7 @@
8 8
9#include <linux/module.h> 9#include <linux/module.h>
10#include <linux/moduleparam.h> 10#include <linux/moduleparam.h>
11#include <linux/interrupt.h>
11#include <sound/pcm.h> 12#include <sound/pcm.h>
12#include <asm/io.h> 13#include <asm/io.h>
13#include "pcsp.h" 14#include "pcsp.h"
@@ -19,6 +20,22 @@ MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround "
19 20
20#define DMIX_WANTS_S16 1 21#define DMIX_WANTS_S16 1
21 22
23/*
24 * Call snd_pcm_period_elapsed in a tasklet
25 * This avoids spinlock messes and long-running irq contexts
26 */
27static void pcsp_call_pcm_elapsed(unsigned long priv)
28{
29 if (atomic_read(&pcsp_chip.timer_active)) {
30 struct snd_pcm_substream *substream;
31 substream = pcsp_chip.playback_substream;
32 if (substream)
33 snd_pcm_period_elapsed(substream);
34 }
35}
36
37static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0);
38
22enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) 39enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
23{ 40{
24 unsigned char timer_cnt, val; 41 unsigned char timer_cnt, val;
@@ -28,41 +45,23 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
28 struct snd_pcm_substream *substream; 45 struct snd_pcm_substream *substream;
29 struct snd_pcm_runtime *runtime; 46 struct snd_pcm_runtime *runtime;
30 struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer); 47 struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
48 unsigned long flags;
31 49
32 if (chip->thalf) { 50 if (chip->thalf) {
33 outb(chip->val61, 0x61); 51 outb(chip->val61, 0x61);
34 chip->thalf = 0; 52 chip->thalf = 0;
35 if (!atomic_read(&chip->timer_active)) 53 if (!atomic_read(&chip->timer_active))
36 return HRTIMER_NORESTART; 54 goto stop;
37 hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer), 55 hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer),
38 ktime_set(0, chip->ns_rem)); 56 ktime_set(0, chip->ns_rem));
39 return HRTIMER_RESTART; 57 return HRTIMER_RESTART;
40 } 58 }
41 59
42 spin_lock_irq(&chip->substream_lock);
43 /* Takashi Iwai says regarding this extra lock:
44
45 If the irq handler handles some data on the DMA buffer, it should
46 do snd_pcm_stream_lock().
47 That protects basically against all races among PCM callbacks, yes.
48 However, there are two remaining issues:
49 1. The substream pointer you try to lock isn't protected _before_
50 this lock yet.
51 2. snd_pcm_period_elapsed() itself acquires the lock.
52 The requirement of another lock is because of 1. When you get
53 chip->playback_substream, it's not protected.
54 Keeping this lock while snd_pcm_period_elapsed() assures the substream
55 is still protected (at least, not released). And the other status is
56 handled properly inside snd_pcm_stream_lock() in
57 snd_pcm_period_elapsed().
58
59 */
60 if (!chip->playback_substream)
61 goto exit_nr_unlock1;
62 substream = chip->playback_substream;
63 snd_pcm_stream_lock(substream);
64 if (!atomic_read(&chip->timer_active)) 60 if (!atomic_read(&chip->timer_active))
65 goto exit_nr_unlock2; 61 goto stop;
62 substream = chip->playback_substream;
63 if (!substream)
64 goto stop;
66 65
67 runtime = substream->runtime; 66 runtime = substream->runtime;
68 fmt_size = snd_pcm_format_physical_width(runtime->format) >> 3; 67 fmt_size = snd_pcm_format_physical_width(runtime->format) >> 3;
@@ -87,6 +86,8 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
87 86
88 period_bytes = snd_pcm_lib_period_bytes(substream); 87 period_bytes = snd_pcm_lib_period_bytes(substream);
89 buffer_bytes = snd_pcm_lib_buffer_bytes(substream); 88 buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
89
90 spin_lock_irqsave(&chip->substream_lock, flags);
90 chip->playback_ptr += PCSP_INDEX_INC() * fmt_size; 91 chip->playback_ptr += PCSP_INDEX_INC() * fmt_size;
91 periods_elapsed = chip->playback_ptr - chip->period_ptr; 92 periods_elapsed = chip->playback_ptr - chip->period_ptr;
92 if (periods_elapsed < 0) { 93 if (periods_elapsed < 0) {
@@ -102,18 +103,15 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
102 * or ALSA will BUG on us. */ 103 * or ALSA will BUG on us. */
103 chip->playback_ptr %= buffer_bytes; 104 chip->playback_ptr %= buffer_bytes;
104 105
105 snd_pcm_stream_unlock(substream);
106
107 if (periods_elapsed) { 106 if (periods_elapsed) {
108 snd_pcm_period_elapsed(substream);
109 chip->period_ptr += periods_elapsed * period_bytes; 107 chip->period_ptr += periods_elapsed * period_bytes;
110 chip->period_ptr %= buffer_bytes; 108 chip->period_ptr %= buffer_bytes;
109 tasklet_schedule(&pcsp_pcm_tasklet);
111 } 110 }
112 111 spin_unlock_irqrestore(&chip->substream_lock, flags);
113 spin_unlock_irq(&chip->substream_lock);
114 112
115 if (!atomic_read(&chip->timer_active)) 113 if (!atomic_read(&chip->timer_active))
116 return HRTIMER_NORESTART; 114 goto stop;
117 115
118 chip->ns_rem = PCSP_PERIOD_NS(); 116 chip->ns_rem = PCSP_PERIOD_NS();
119 ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem); 117 ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
@@ -122,10 +120,7 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
122 ktime_set(0, ns)); 120 ktime_set(0, ns));
123 return HRTIMER_RESTART; 121 return HRTIMER_RESTART;
124 122
125exit_nr_unlock2: 123 stop:
126 snd_pcm_stream_unlock(substream);
127exit_nr_unlock1:
128 spin_unlock_irq(&chip->substream_lock);
129 return HRTIMER_NORESTART; 124 return HRTIMER_NORESTART;
130} 125}
131 126
@@ -165,26 +160,35 @@ static void pcsp_stop_playing(struct snd_pcsp *chip)
165 spin_unlock(&i8253_lock); 160 spin_unlock(&i8253_lock);
166} 161}
167 162
163/*
164 * Force to stop and sync the stream
165 */
166void pcsp_sync_stop(struct snd_pcsp *chip)
167{
168 local_irq_disable();
169 pcsp_stop_playing(chip);
170 local_irq_enable();
171 hrtimer_cancel(&chip->timer);
172 tasklet_kill(&pcsp_pcm_tasklet);
173}
174
168static int snd_pcsp_playback_close(struct snd_pcm_substream *substream) 175static int snd_pcsp_playback_close(struct snd_pcm_substream *substream)
169{ 176{
170 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 177 struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
171#if PCSP_DEBUG 178#if PCSP_DEBUG
172 printk(KERN_INFO "PCSP: close called\n"); 179 printk(KERN_INFO "PCSP: close called\n");
173#endif 180#endif
174 if (atomic_read(&chip->timer_active)) { 181 pcsp_sync_stop(chip);
175 printk(KERN_ERR "PCSP: timer still active\n");
176 pcsp_stop_playing(chip);
177 }
178 spin_lock_irq(&chip->substream_lock);
179 chip->playback_substream = NULL; 182 chip->playback_substream = NULL;
180 spin_unlock_irq(&chip->substream_lock);
181 return 0; 183 return 0;
182} 184}
183 185
184static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream, 186static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
185 struct snd_pcm_hw_params *hw_params) 187 struct snd_pcm_hw_params *hw_params)
186{ 188{
189 struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
187 int err; 190 int err;
191 pcsp_sync_stop(chip);
188 err = snd_pcm_lib_malloc_pages(substream, 192 err = snd_pcm_lib_malloc_pages(substream,
189 params_buffer_bytes(hw_params)); 193 params_buffer_bytes(hw_params));
190 if (err < 0) 194 if (err < 0)
@@ -194,9 +198,11 @@ static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream,
194 198
195static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream) 199static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream)
196{ 200{
201 struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
197#if PCSP_DEBUG 202#if PCSP_DEBUG
198 printk(KERN_INFO "PCSP: hw_free called\n"); 203 printk(KERN_INFO "PCSP: hw_free called\n");
199#endif 204#endif
205 pcsp_sync_stop(chip);
200 return snd_pcm_lib_free_pages(substream); 206 return snd_pcm_lib_free_pages(substream);
201} 207}
202 208
@@ -212,6 +218,7 @@ static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
212 snd_pcm_lib_period_bytes(substream), 218 snd_pcm_lib_period_bytes(substream),
213 substream->runtime->periods); 219 substream->runtime->periods);
214#endif 220#endif
221 pcsp_sync_stop(chip);
215 chip->playback_ptr = 0; 222 chip->playback_ptr = 0;
216 chip->period_ptr = 0; 223 chip->period_ptr = 0;
217 return 0; 224 return 0;
@@ -242,7 +249,11 @@ static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream
242 *substream) 249 *substream)
243{ 250{
244 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 251 struct snd_pcsp *chip = snd_pcm_substream_chip(substream);
245 return bytes_to_frames(substream->runtime, chip->playback_ptr); 252 unsigned int pos;
253 spin_lock(&chip->substream_lock);
254 pos = chip->playback_ptr;
255 spin_unlock(&chip->substream_lock);
256 return bytes_to_frames(substream->runtime, pos);
246} 257}
247 258
248static struct snd_pcm_hardware snd_pcsp_playback = { 259static struct snd_pcm_hardware snd_pcsp_playback = {
@@ -279,9 +290,7 @@ static int snd_pcsp_playback_open(struct snd_pcm_substream *substream)
279 return -EBUSY; 290 return -EBUSY;
280 } 291 }
281 runtime->hw = snd_pcsp_playback; 292 runtime->hw = snd_pcsp_playback;
282 spin_lock_irq(&chip->substream_lock);
283 chip->playback_substream = substream; 293 chip->playback_substream = substream;
284 spin_unlock_irq(&chip->substream_lock);
285 return 0; 294 return 0;
286} 295}
287 296