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/gfp.h> 11#include <linux/moduleparam.h> 12#include <linux/interrupt.h> 13#include <linux/io.h> 14#include <sound/pcm.h> 15#include "pcsp.h" 16 17static bool nforce_wa; 18module_param(nforce_wa, bool, 0444); 19MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround " 20 "(expect bad sound)"); 21 22#define DMIX_WANTS_S16 1 23 24/* 25 * Call snd_pcm_period_elapsed in a tasklet 26 * This avoids spinlock messes and long-running irq contexts 27 */ 28static void pcsp_call_pcm_elapsed(unsigned long priv) 29{ 30 if (atomic_read(&pcsp_chip.timer_active)) { 31 struct snd_pcm_substream *substream; 32 substream = pcsp_chip.playback_substream; 33 if (substream) 34 snd_pcm_period_elapsed(substream); 35 } 36} 37 38static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0); 39 40/* write the port and returns the next expire time in ns; 41 * called at the trigger-start and in hrtimer callback 42 */ 43static u64 pcsp_timer_update(struct snd_pcsp *chip) 44{ 45 unsigned char timer_cnt, val; 46 u64 ns; 47 struct snd_pcm_substream *substream; 48 struct snd_pcm_runtime *runtime; 49 unsigned long flags; 50 51 if (chip->thalf) { 52 outb(chip->val61, 0x61); 53 chip->thalf = 0; 54 return chip->ns_rem; 55 } 56 57 substream = chip->playback_substream; 58 if (!substream) 59 return 0; 60 61 runtime = substream->runtime; 62 /* assume it is mono! */ 63 val = runtime->dma_area[chip->playback_ptr + chip->fmt_size - 1]; 64 if (chip->is_signed) 65 val ^= 0x80; 66 timer_cnt = val * CUR_DIV() / 256; 67 68 if (timer_cnt && chip->enable) { 69 raw_spin_lock_irqsave(&i8253_lock, flags); 70 if (!nforce_wa) { 71 outb_p(chip->val61, 0x61); 72 outb_p(timer_cnt, 0x42); 73 outb(chip->val61 ^ 1, 0x61); 74 } else { 75 outb(chip->val61 ^ 2, 0x61); 76 chip->thalf = 1; 77 } 78 raw_spin_unlock_irqrestore(&i8253_lock, flags); 79 } 80 81 chip->ns_rem = PCSP_PERIOD_NS(); 82 ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem); 83 chip->ns_rem -= ns; 84 return ns; 85} 86 87static void pcsp_pointer_update(struct snd_pcsp *chip) 88{ 89 struct snd_pcm_substream *substream; 90 size_t period_bytes, buffer_bytes; 91 int periods_elapsed; 92 unsigned long flags; 93 94 /* update the playback position */ 95 substream = chip->playback_substream; 96 if (!substream) 97 return; 98 99 period_bytes = snd_pcm_lib_period_bytes(substream); 100 buffer_bytes = snd_pcm_lib_buffer_bytes(substream); 101 102 spin_lock_irqsave(&chip->substream_lock, flags); 103 chip->playback_ptr += PCSP_INDEX_INC() * chip->fmt_size; 104 periods_elapsed = chip->playback_ptr - chip->period_ptr; 105 if (periods_elapsed < 0) { 106#if PCSP_DEBUG 107 printk(KERN_INFO "PCSP: buffer_bytes mod period_bytes != 0 ? " 108 "(%zi %zi %zi)\n", 109 chip->playback_ptr, period_bytes, buffer_bytes); 110#endif 111 periods_elapsed += buffer_bytes; 112 } 113 periods_elapsed /= period_bytes; 114 /* wrap the pointer _before_ calling snd_pcm_period_elapsed(), 115 * or ALSA will BUG on us. */ 116 chip->playback_ptr %= buffer_bytes; 117 118 if (periods_elapsed) { 119 chip->period_ptr += periods_elapsed * period_bytes; 120 chip->period_ptr %= buffer_bytes; 121 } 122 spin_unlock_irqrestore(&chip->substream_lock, flags); 123 124 if (periods_elapsed) 125 tasklet_schedule(&pcsp_pcm_tasklet); 126} 127 128enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) 129{ 130 struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer); 131 int pointer_update; 132 u64 ns; 133 134 if (!atomic_read(&chip->timer_active) || !chip->playback_substream) 135 return HRTIMER_NORESTART; 136 137 pointer_update = !chip->thalf; 138 ns = pcsp_timer_update(chip); 139 if (!ns) { 140 printk(KERN_WARNING "PCSP: unexpected stop\n"); 141 return HRTIMER_NORESTART; 142 } 143 144 if (pointer_update) 145 pcsp_pointer_update(chip); 146 147 hrtimer_forward(handle, hrtimer_get_expires(handle), ns_to_ktime(ns)); 148 149 return HRTIMER_RESTART; 150} 151 152static int pcsp_start_playing(struct snd_pcsp *chip) 153{ 154#if PCSP_DEBUG 155 printk(KERN_INFO "PCSP: start_playing called\n"); 156#endif 157 if (atomic_read(&chip->timer_active)) { 158 printk(KERN_ERR "PCSP: Timer already active\n"); 159 return -EIO; 160 } 161 162 raw_spin_lock(&i8253_lock); 163 chip->val61 = inb(0x61) | 0x03; 164 outb_p(0x92, 0x43); /* binary, mode 1, LSB only, ch 2 */ 165 raw_spin_unlock(&i8253_lock); 166 atomic_set(&chip->timer_active, 1); 167 chip->thalf = 0; 168 169 hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL); 170 return 0; 171} 172 173static 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 raw_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 raw_spin_unlock(&i8253_lock); 187} 188 189/* 190 * Force to stop and sync the stream 191 */ 192void pcsp_sync_stop(struct snd_pcsp *chip) 193{ 194 local_irq_disable(); 195 pcsp_stop_playing(chip); 196 local_irq_enable(); 197 hrtimer_cancel(&chip->timer); 198 tasklet_kill(&pcsp_pcm_tasklet); 199} 200 201static int snd_pcsp_playback_close(struct snd_pcm_substream *substream) 202{ 203 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 204#if PCSP_DEBUG 205 printk(KERN_INFO "PCSP: close called\n"); 206#endif 207 pcsp_sync_stop(chip); 208 chip->playback_substream = NULL; 209 return 0; 210} 211 212static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream, 213 struct snd_pcm_hw_params *hw_params) 214{ 215 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 216 int err; 217 pcsp_sync_stop(chip); 218 err = snd_pcm_lib_malloc_pages(substream, 219 params_buffer_bytes(hw_params)); 220 if (err < 0) 221 return err; 222 return 0; 223} 224 225static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream) 226{ 227 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 228#if PCSP_DEBUG 229 printk(KERN_INFO "PCSP: hw_free called\n"); 230#endif 231 pcsp_sync_stop(chip); 232 return snd_pcm_lib_free_pages(substream); 233} 234 235static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream) 236{ 237 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 238 pcsp_sync_stop(chip); 239 chip->playback_ptr = 0; 240 chip->period_ptr = 0; 241 chip->fmt_size = 242 snd_pcm_format_physical_width(substream->runtime->format) >> 3; 243 chip->is_signed = snd_pcm_format_signed(substream->runtime->format); 244#if PCSP_DEBUG 245 printk(KERN_INFO "PCSP: prepare called, " 246 "size=%zi psize=%zi f=%zi f1=%i fsize=%i\n", 247 snd_pcm_lib_buffer_bytes(substream), 248 snd_pcm_lib_period_bytes(substream), 249 snd_pcm_lib_buffer_bytes(substream) / 250 snd_pcm_lib_period_bytes(substream), 251 substream->runtime->periods, 252 chip->fmt_size); 253#endif 254 return 0; 255} 256 257static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd) 258{ 259 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 260#if PCSP_DEBUG 261 printk(KERN_INFO "PCSP: trigger called\n"); 262#endif 263 switch (cmd) { 264 case SNDRV_PCM_TRIGGER_START: 265 case SNDRV_PCM_TRIGGER_RESUME: 266 return pcsp_start_playing(chip); 267 case SNDRV_PCM_TRIGGER_STOP: 268 case SNDRV_PCM_TRIGGER_SUSPEND: 269 pcsp_stop_playing(chip); 270 break; 271 default: 272 return -EINVAL; 273 } 274 return 0; 275} 276 277static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream 278 *substream) 279{ 280 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 281 unsigned int pos; 282 spin_lock(&chip->substream_lock); 283 pos = chip->playback_ptr; 284 spin_unlock(&chip->substream_lock); 285 return bytes_to_frames(substream->runtime, pos); 286} 287 288static struct snd_pcm_hardware snd_pcsp_playback = { 289 .info = (SNDRV_PCM_INFO_INTERLEAVED | 290 SNDRV_PCM_INFO_HALF_DUPLEX | 291 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), 292 .formats = (SNDRV_PCM_FMTBIT_U8 293#if DMIX_WANTS_S16 294 | SNDRV_PCM_FMTBIT_S16_LE 295#endif 296 ), 297 .rates = SNDRV_PCM_RATE_KNOT, 298 .rate_min = PCSP_DEFAULT_SRATE, 299 .rate_max = PCSP_DEFAULT_SRATE, 300 .channels_min = 1, 301 .channels_max = 1, 302 .buffer_bytes_max = PCSP_BUFFER_SIZE, 303 .period_bytes_min = 64, 304 .period_bytes_max = PCSP_MAX_PERIOD_SIZE, 305 .periods_min = 2, 306 .periods_max = PCSP_MAX_PERIODS, 307 .fifo_size = 0, 308}; 309 310static int snd_pcsp_playback_open(struct snd_pcm_substream *substream) 311{ 312 struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 313 struct snd_pcm_runtime *runtime = substream->runtime; 314#if PCSP_DEBUG 315 printk(KERN_INFO "PCSP: open called\n"); 316#endif 317 if (atomic_read(&chip->timer_active)) { 318 printk(KERN_ERR "PCSP: still active!!\n"); 319 return -EBUSY; 320 } 321 runtime->hw = snd_pcsp_playback; 322 chip->playback_substream = substream; 323 return 0; 324} 325 326static struct snd_pcm_ops snd_pcsp_playback_ops = { 327 .open = snd_pcsp_playback_open, 328 .close = snd_pcsp_playback_close, 329 .ioctl = snd_pcm_lib_ioctl, 330 .hw_params = snd_pcsp_playback_hw_params, 331 .hw_free = snd_pcsp_playback_hw_free, 332 .prepare = snd_pcsp_playback_prepare, 333 .trigger = snd_pcsp_trigger, 334 .pointer = snd_pcsp_playback_pointer, 335}; 336 337int snd_pcsp_new_pcm(struct snd_pcsp *chip) 338{ 339 int err; 340 341 err = snd_pcm_new(chip->card, "pcspeaker", 0, 1, 0, &chip->pcm); 342 if (err < 0) 343 return err; 344 345 snd_pcm_set_ops(chip->pcm, SNDRV_PCM_STREAM_PLAYBACK, 346 &snd_pcsp_playback_ops); 347 348 chip->pcm->private_data = chip; 349 chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; 350 strcpy(chip->pcm->name, "pcsp"); 351 352 snd_pcm_lib_preallocate_pages_for_all(chip->pcm, 353 SNDRV_DMA_TYPE_CONTINUOUS, 354 snd_dma_continuous_data 355 (GFP_KERNEL), PCSP_BUFFER_SIZE, 356 PCSP_BUFFER_SIZE); 357 358 return 0; 359} 360