1 /* 2 * PC-Speaker driver for Linux 3 * 4 * Copyright (C) 1993-1997 Michael Beck 5 * Copyright (C) 1997-2001 David Woodhouse 6 * Copyright (C) 2001-2008 Stas Sergeev 7 */ 8 9 #include <linux/module.h> 10 #include <linux/moduleparam.h> 11 #include <sound/pcm.h> 12 #include <linux/interrupt.h> 13 #include <asm/io.h> 14 #include "pcsp.h" 15 16 static int nforce_wa; 17 module_param(nforce_wa, bool, 0444); 18 MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround " 19 "(expect bad sound)"); 20 21 static void pcsp_start_timer(unsigned long dummy) 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 46 enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) 47 { 48 unsigned long flags; 49 unsigned char timer_cnt, val; 50 int periods_elapsed; 51 u64 ns; 52 size_t period_bytes, buffer_bytes; 53 struct snd_pcm_substream *substream; 54 struct snd_pcm_runtime *runtime; 55 struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer); 56 57 if (chip->thalf) { 58 outb(chip->val61, 0x61); 59 chip->thalf = 0; 60 if (!atomic_read(&chip->timer_active)) 61 return HRTIMER_NORESTART; 62 hrtimer_forward(&chip->timer, chip->timer.expires, 63 ktime_set(0, chip->ns_rem)); 64 return HRTIMER_RESTART; 65 } 66 67 /* hrtimer calls us from both hardirq and softirq contexts, 68 * so irqsave :( */ 69 spin_lock_irqsave(&chip->substream_lock, flags); 70 /* Takashi Iwai says regarding this extra lock: 71 72 If the irq handler handles some data on the DMA buffer, it should 73 do snd_pcm_stream_lock(). 74 That protects basically against all races among PCM callbacks, yes. 75 However, there are two remaining issues: 76 1. The substream pointer you try to lock isn't protected _before_ 77 this lock yet. 78 2. snd_pcm_period_elapsed() itself acquires the lock. 79 The requirement of another lock is because of 1. When you get 80 chip->playback_substream, it's not protected. 81 Keeping this lock while snd_pcm_period_elapsed() assures the substream 82 is still protected (at least, not released). And the other status is 83 handled properly inside snd_pcm_stream_lock() in 84 snd_pcm_period_elapsed(). 85 86 */ 87 if (!chip->playback_substream) 88 goto exit_nr_unlock1; 89 substream = chip->playback_substream; 90 snd_pcm_stream_lock(substream); 91 if (!atomic_read(&chip->timer_active)) 92 goto exit_nr_unlock2; 93 94 runtime = substream->runtime; 95 /* assume it is u8 mono */ 96 val = runtime->dma_area[chip->playback_ptr]; 97 timer_cnt = val * CUR_DIV() / 256; 98 99 if (timer_cnt && chip->enable) { 100 spin_lock(&i8253_lock); 101 if (!nforce_wa) { 102 outb_p(chip->val61, 0x61); 103 outb_p(timer_cnt, 0x42); 104 outb(chip->val61 ^ 1, 0x61); 105 } else { 106 outb(chip->val61 ^ 2, 0x61); 107 chip->thalf = 1; 108 } 109 spin_unlock(&i8253_lock); 110 } 111 112 period_bytes = snd_pcm_lib_period_bytes(substream); 113 buffer_bytes = snd_pcm_lib_buffer_bytes(substream); 114 chip->playback_ptr += PCSP_INDEX_INC(); 115 periods_elapsed = chip->playback_ptr - chip->period_ptr; 116 if (periods_elapsed < 0) { 117 printk(KERN_WARNING "PCSP: playback_ptr inconsistent " 118 "(%zi %zi %zi)\n", 119 chip->playback_ptr, period_bytes, buffer_bytes); 120 periods_elapsed += buffer_bytes; 121 } 122 periods_elapsed /= period_bytes; 123 /* wrap the pointer _before_ calling snd_pcm_period_elapsed(), 124 * or ALSA will BUG on us. */ 125 chip->playback_ptr %= buffer_bytes; 126 127 snd_pcm_stream_unlock(substream); 128 129 if (periods_elapsed) { 130 snd_pcm_period_elapsed(substream); 131 chip->period_ptr += periods_elapsed * period_bytes; 132 chip->period_ptr %= buffer_bytes; 133 } 134 135 spin_unlock_irqrestore(&chip->substream_lock, flags); 136 137 if (!atomic_read(&chip->timer_active)) 138 return HRTIMER_NORESTART; 139 140 chip->ns_rem = PCSP_PERIOD_NS(); 141 ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem); 142 chip->ns_rem -= ns; 143 hrtimer_forward(&chip->timer, chip->timer.expires, ktime_set(0, ns)); 144 return HRTIMER_RESTART; 145 146 exit_nr_unlock2: 147 snd_pcm_stream_unlock(substream); 148 exit_nr_unlock1: 149 spin_unlock_irqrestore(&chip->substream_lock, flags); 150 return HRTIMER_NORESTART; 151 } 152 153 static void pcsp_start_playing(struct snd_pcsp *chip) 154 { 155 #if PCSP_DEBUG 156 printk(KERN_INFO "PCSP: start_playing called\n"); 157 #endif 158 if (atomic_read(&chip->timer_active)) { 159 printk(KERN_ERR "PCSP: Timer already active\n"); 160 return; 161 } 162 163 spin_lock(&i8253_lock); 164 chip->val61 = inb(0x61) | 0x03; 165 outb_p(0x92, 0x43); /* binary, mode 1, LSB only, ch 2 */ 166 spin_unlock(&i8253_lock); 167 atomic_set(&chip->timer_active, 1); 168 chip->thalf = 0; 169 170 tasklet_schedule(&pcsp_start_timer_tasklet); 171 } 172 173 static void pcsp_stop_playing(struct snd_pcsp *chip) 174 { 175 #if PCSP_DEBUG 176 printk(KERN_INFO "PCSP: stop_playing called\n"); 177 #endif 178 if (!atomic_read(&chip->timer_active)) 179 return; 180 181 atomic_set(&chip->timer_active, 0); 182 spin_lock(&i8253_lock); 183 /* restore the timer */ 184 outb_p(0xb6, 0x43); /* binary, mode 3, LSB/MSB, ch 2 */ 185 outb(chip->val61 & 0xFC, 0x61); 186 spin_unlock(&i8253_lock); 187 } 188 189 static int snd_pcsp_playback_close(struct snd_pcm_substream *substream) 190 { 191 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 192 #if PCSP_DEBUG 193 printk(KERN_INFO "PCSP: close called\n"); 194 #endif 195 if (atomic_read(&chip->timer_active)) { 196 printk(KERN_ERR "PCSP: timer still active\n"); 197 pcsp_stop_playing(chip); 198 } 199 spin_lock_irq(&chip->substream_lock); 200 chip->playback_substream = NULL; 201 spin_unlock_irq(&chip->substream_lock); 202 return 0; 203 } 204 205 static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream, 206 struct snd_pcm_hw_params *hw_params) 207 { 208 int err; 209 err = snd_pcm_lib_malloc_pages(substream, 210 params_buffer_bytes(hw_params)); 211 if (err < 0) 212 return err; 213 return 0; 214 } 215 216 static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream) 217 { 218 #if PCSP_DEBUG 219 printk(KERN_INFO "PCSP: hw_free called\n"); 220 #endif 221 return snd_pcm_lib_free_pages(substream); 222 } 223 224 static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream) 225 { 226 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 227 #if PCSP_DEBUG 228 printk(KERN_INFO "PCSP: prepare called, " 229 "size=%zi psize=%zi f=%zi f1=%i\n", 230 snd_pcm_lib_buffer_bytes(substream), 231 snd_pcm_lib_period_bytes(substream), 232 snd_pcm_lib_buffer_bytes(substream) / 233 snd_pcm_lib_period_bytes(substream), 234 substream->runtime->periods); 235 #endif 236 chip->playback_ptr = 0; 237 chip->period_ptr = 0; 238 return 0; 239 } 240 241 static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd) 242 { 243 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 244 #if PCSP_DEBUG 245 printk(KERN_INFO "PCSP: trigger called\n"); 246 #endif 247 switch (cmd) { 248 case SNDRV_PCM_TRIGGER_START: 249 case SNDRV_PCM_TRIGGER_RESUME: 250 pcsp_start_playing(chip); 251 break; 252 case SNDRV_PCM_TRIGGER_STOP: 253 case SNDRV_PCM_TRIGGER_SUSPEND: 254 pcsp_stop_playing(chip); 255 break; 256 default: 257 return -EINVAL; 258 } 259 return 0; 260 } 261 262 static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream 263 *substream) 264 { 265 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 266 return bytes_to_frames(substream->runtime, chip->playback_ptr); 267 } 268 269 static struct snd_pcm_hardware snd_pcsp_playback = { 270 .info = (SNDRV_PCM_INFO_INTERLEAVED | 271 SNDRV_PCM_INFO_HALF_DUPLEX | 272 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), 273 .formats = SNDRV_PCM_FMTBIT_U8, 274 .rates = SNDRV_PCM_RATE_KNOT, 275 .rate_min = PCSP_DEFAULT_SRATE, 276 .rate_max = PCSP_DEFAULT_SRATE, 277 .channels_min = 1, 278 .channels_max = 1, 279 .buffer_bytes_max = PCSP_BUFFER_SIZE, 280 .period_bytes_min = 64, 281 .period_bytes_max = PCSP_MAX_PERIOD_SIZE, 282 .periods_min = 2, 283 .periods_max = PCSP_MAX_PERIODS, 284 .fifo_size = 0, 285 }; 286 287 static int snd_pcsp_playback_open(struct snd_pcm_substream *substream) 288 { 289 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 290 struct snd_pcm_runtime *runtime = substream->runtime; 291 #if PCSP_DEBUG 292 printk(KERN_INFO "PCSP: open called\n"); 293 #endif 294 if (atomic_read(&chip->timer_active)) { 295 printk(KERN_ERR "PCSP: still active!!\n"); 296 return -EBUSY; 297 } 298 runtime->hw = snd_pcsp_playback; 299 spin_lock_irq(&chip->substream_lock); 300 chip->playback_substream = substream; 301 spin_unlock_irq(&chip->substream_lock); 302 return 0; 303 } 304 305 static struct snd_pcm_ops snd_pcsp_playback_ops = { 306 .open = snd_pcsp_playback_open, 307 .close = snd_pcsp_playback_close, 308 .ioctl = snd_pcm_lib_ioctl, 309 .hw_params = snd_pcsp_playback_hw_params, 310 .hw_free = snd_pcsp_playback_hw_free, 311 .prepare = snd_pcsp_playback_prepare, 312 .trigger = snd_pcsp_trigger, 313 .pointer = snd_pcsp_playback_pointer, 314 }; 315 316 int __devinit snd_pcsp_new_pcm(struct snd_pcsp *chip) 317 { 318 int err; 319 320 err = snd_pcm_new(chip->card, "pcspeaker", 0, 1, 0, &chip->pcm); 321 if (err < 0) 322 return err; 323 324 snd_pcm_set_ops(chip->pcm, SNDRV_PCM_STREAM_PLAYBACK, 325 &snd_pcsp_playback_ops); 326 327 chip->pcm->private_data = chip; 328 chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; 329 strcpy(chip->pcm->name, "pcsp"); 330 331 snd_pcm_lib_preallocate_pages_for_all(chip->pcm, 332 SNDRV_DMA_TYPE_CONTINUOUS, 333 snd_dma_continuous_data 334 (GFP_KERNEL), PCSP_BUFFER_SIZE, 335 PCSP_BUFFER_SIZE); 336 337 return 0; 338 } 339