diff options
author | Jonathan Corbet <corbet@lwn.net> | 2008-07-14 17:29:34 -0400 |
---|---|---|
committer | Jonathan Corbet <corbet@lwn.net> | 2008-07-14 17:29:34 -0400 |
commit | 2fceef397f9880b212a74c418290ce69e7ac00eb (patch) | |
tree | d9cc09ab992825ef7fede4a688103503e3caf655 /sound/drivers | |
parent | feae1ef116ed381625d3731c5ae4f4ebcb3fa302 (diff) | |
parent | bce7f793daec3e65ec5c5705d2457b81fe7b5725 (diff) |
Merge commit 'v2.6.26' into bkl-removal
Diffstat (limited to 'sound/drivers')
-rw-r--r-- | sound/drivers/Kconfig | 15 | ||||
-rw-r--r-- | sound/drivers/pcsp/pcsp.c | 2 | ||||
-rw-r--r-- | sound/drivers/pcsp/pcsp.h | 6 | ||||
-rw-r--r-- | sound/drivers/pcsp/pcsp_lib.c | 58 | ||||
-rw-r--r-- | sound/drivers/pcsp/pcsp_mixer.c | 3 |
5 files changed, 40 insertions, 44 deletions
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 379bcb074463..602b58e3b55d 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig | |||
@@ -5,9 +5,10 @@ menu "Generic devices" | |||
5 | 5 | ||
6 | 6 | ||
7 | config SND_PCSP | 7 | config SND_PCSP |
8 | tristate "PC-Speaker support" | 8 | tristate "PC-Speaker support (READ HELP!)" |
9 | depends on PCSPKR_PLATFORM && X86_PC && HIGH_RES_TIMERS | 9 | depends on PCSPKR_PLATFORM && X86_PC && HIGH_RES_TIMERS |
10 | depends on INPUT | 10 | depends on INPUT |
11 | depends on EXPERIMENTAL | ||
11 | depends on SND | 12 | depends on SND |
12 | select SND_PCM | 13 | select SND_PCM |
13 | help | 14 | help |
@@ -18,11 +19,21 @@ config SND_PCSP | |||
18 | 19 | ||
19 | You can compile this as a module which will be called snd-pcsp. | 20 | You can compile this as a module which will be called snd-pcsp. |
20 | 21 | ||
22 | WARNING: if you already have a soundcard, enabling this | ||
23 | driver may lead to a problem. Namely, it may get loaded | ||
24 | before the other sound driver of yours, making the | ||
25 | pc-speaker a default sound device. Which is likely not | ||
26 | what you want. To make this driver play nicely with other | ||
27 | sound driver, you can add this into your /etc/modprobe.conf: | ||
28 | options snd-pcsp index=2 | ||
29 | |||
21 | You don't need this driver if you only want your pc-speaker to beep. | 30 | You don't need this driver if you only want your pc-speaker to beep. |
22 | You don't need this driver if you have a tablet piezo beeper | 31 | You don't need this driver if you have a tablet piezo beeper |
23 | in your PC instead of the real speaker. | 32 | in your PC instead of the real speaker. |
24 | 33 | ||
25 | It should not hurt to say Y or M here in all other cases. | 34 | Say N if you have a sound card. |
35 | Say M if you don't. | ||
36 | Say Y only if you really know what you do. | ||
26 | 37 | ||
27 | config SND_MPU401_UART | 38 | config SND_MPU401_UART |
28 | tristate | 39 | tristate |
diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c index 54a1f9036c66..1899cf0685bc 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_IRQSAFE; | 99 | pcsp_chip.timer.cb_mode = HRTIMER_CB_SOFTIRQ; |
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); |
diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h index f07cc1ee1fe7..1d661f795e8c 100644 --- a/sound/drivers/pcsp/pcsp.h +++ b/sound/drivers/pcsp/pcsp.h | |||
@@ -24,7 +24,8 @@ static DEFINE_SPINLOCK(i8253_lock); | |||
24 | /* default timer freq for PC-Speaker: 18643 Hz */ | 24 | /* default timer freq for PC-Speaker: 18643 Hz */ |
25 | #define DIV_18KHZ 64 | 25 | #define DIV_18KHZ 64 |
26 | #define MAX_DIV DIV_18KHZ | 26 | #define MAX_DIV DIV_18KHZ |
27 | #define CUR_DIV() (MAX_DIV >> chip->treble) | 27 | #define CALC_DIV(d) (MAX_DIV >> (d)) |
28 | #define CUR_DIV() CALC_DIV(chip->treble) | ||
28 | #define PCSP_MAX_TREBLE 1 | 29 | #define PCSP_MAX_TREBLE 1 |
29 | 30 | ||
30 | /* unfortunately, with hrtimers 37KHz does not work very well :( */ | 31 | /* unfortunately, with hrtimers 37KHz does not work very well :( */ |
@@ -36,7 +37,8 @@ static DEFINE_SPINLOCK(i8253_lock); | |||
36 | #define PCSP_DEFAULT_SDIV (DIV_18KHZ >> 1) | 37 | #define PCSP_DEFAULT_SDIV (DIV_18KHZ >> 1) |
37 | #define PCSP_DEFAULT_SRATE (PIT_TICK_RATE / PCSP_DEFAULT_SDIV) | 38 | #define PCSP_DEFAULT_SRATE (PIT_TICK_RATE / PCSP_DEFAULT_SDIV) |
38 | #define PCSP_INDEX_INC() (1 << (PCSP_MAX_TREBLE - chip->treble)) | 39 | #define PCSP_INDEX_INC() (1 << (PCSP_MAX_TREBLE - chip->treble)) |
39 | #define PCSP_RATE() (PIT_TICK_RATE / CUR_DIV()) | 40 | #define PCSP_CALC_RATE(i) (PIT_TICK_RATE / CALC_DIV(i)) |
41 | #define PCSP_RATE() PCSP_CALC_RATE(chip->treble) | ||
40 | #define PCSP_MIN_RATE__1 MAX_DIV/PIT_TICK_RATE | 42 | #define PCSP_MIN_RATE__1 MAX_DIV/PIT_TICK_RATE |
41 | #define PCSP_MAX_RATE__1 MIN_DIV/PIT_TICK_RATE | 43 | #define PCSP_MAX_RATE__1 MIN_DIV/PIT_TICK_RATE |
42 | #define PCSP_MAX_PERIOD_NS (1000000000ULL * PCSP_MIN_RATE__1) | 44 | #define PCSP_MAX_PERIOD_NS (1000000000ULL * PCSP_MIN_RATE__1) |
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, |
diff --git a/sound/drivers/pcsp/pcsp_mixer.c b/sound/drivers/pcsp/pcsp_mixer.c index 64a695fef74e..caeb0f57fcca 100644 --- a/sound/drivers/pcsp/pcsp_mixer.c +++ b/sound/drivers/pcsp/pcsp_mixer.c | |||
@@ -50,7 +50,8 @@ static int pcsp_treble_info(struct snd_kcontrol *kcontrol, | |||
50 | uinfo->value.enumerated.items = chip->max_treble + 1; | 50 | uinfo->value.enumerated.items = chip->max_treble + 1; |
51 | if (uinfo->value.enumerated.item > chip->max_treble) | 51 | if (uinfo->value.enumerated.item > chip->max_treble) |
52 | uinfo->value.enumerated.item = chip->max_treble; | 52 | uinfo->value.enumerated.item = chip->max_treble; |
53 | sprintf(uinfo->value.enumerated.name, "%d", PCSP_RATE()); | 53 | sprintf(uinfo->value.enumerated.name, "%d", |
54 | PCSP_CALC_RATE(uinfo->value.enumerated.item)); | ||
54 | return 0; | 55 | return 0; |
55 | } | 56 | } |
56 | 57 | ||