aboutsummaryrefslogtreecommitdiffstats
path: root/sound/drivers
diff options
context:
space:
mode:
authorJonathan Corbet <corbet@lwn.net>2008-07-14 17:29:34 -0400
committerJonathan Corbet <corbet@lwn.net>2008-07-14 17:29:34 -0400
commit2fceef397f9880b212a74c418290ce69e7ac00eb (patch)
treed9cc09ab992825ef7fede4a688103503e3caf655 /sound/drivers
parentfeae1ef116ed381625d3731c5ae4f4ebcb3fa302 (diff)
parentbce7f793daec3e65ec5c5705d2457b81fe7b5725 (diff)
Merge commit 'v2.6.26' into bkl-removal
Diffstat (limited to 'sound/drivers')
-rw-r--r--sound/drivers/Kconfig15
-rw-r--r--sound/drivers/pcsp/pcsp.c2
-rw-r--r--sound/drivers/pcsp/pcsp.h6
-rw-r--r--sound/drivers/pcsp/pcsp_lib.c58
-rw-r--r--sound/drivers/pcsp/pcsp_mixer.c3
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
7config SND_PCSP 7config 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
27config SND_MPU401_UART 38config 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);
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,
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