19ab4d072SStas Sergeev /* 29ab4d072SStas Sergeev * PC-Speaker driver for Linux 39ab4d072SStas Sergeev * 49ab4d072SStas Sergeev * Copyright (C) 1993-1997 Michael Beck 59ab4d072SStas Sergeev * Copyright (C) 1997-2001 David Woodhouse 69ab4d072SStas Sergeev * Copyright (C) 2001-2008 Stas Sergeev 79ab4d072SStas Sergeev */ 89ab4d072SStas Sergeev 99ab4d072SStas Sergeev #include <linux/module.h> 109ab4d072SStas Sergeev #include <linux/moduleparam.h> 11*96c7d478STakashi Iwai #include <linux/interrupt.h> 129ab4d072SStas Sergeev #include <sound/pcm.h> 139ab4d072SStas Sergeev #include <asm/io.h> 149ab4d072SStas Sergeev #include "pcsp.h" 159ab4d072SStas Sergeev 169ab4d072SStas Sergeev static int nforce_wa; 179ab4d072SStas Sergeev module_param(nforce_wa, bool, 0444); 189ab4d072SStas Sergeev MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround " 199ab4d072SStas Sergeev "(expect bad sound)"); 209ab4d072SStas Sergeev 214dfd7954SStas Sergeev #define DMIX_WANTS_S16 1 224dfd7954SStas Sergeev 23*96c7d478STakashi Iwai /* 24*96c7d478STakashi Iwai * Call snd_pcm_period_elapsed in a tasklet 25*96c7d478STakashi Iwai * This avoids spinlock messes and long-running irq contexts 26*96c7d478STakashi Iwai */ 27*96c7d478STakashi Iwai static void pcsp_call_pcm_elapsed(unsigned long priv) 28*96c7d478STakashi Iwai { 29*96c7d478STakashi Iwai if (atomic_read(&pcsp_chip.timer_active)) { 30*96c7d478STakashi Iwai struct snd_pcm_substream *substream; 31*96c7d478STakashi Iwai substream = pcsp_chip.playback_substream; 32*96c7d478STakashi Iwai if (substream) 33*96c7d478STakashi Iwai snd_pcm_period_elapsed(substream); 34*96c7d478STakashi Iwai } 35*96c7d478STakashi Iwai } 36*96c7d478STakashi Iwai 37*96c7d478STakashi Iwai static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0); 38*96c7d478STakashi Iwai 399ab4d072SStas Sergeev enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) 409ab4d072SStas Sergeev { 419ab4d072SStas Sergeev unsigned char timer_cnt, val; 424dfd7954SStas Sergeev int fmt_size, periods_elapsed; 439ab4d072SStas Sergeev u64 ns; 449ab4d072SStas Sergeev size_t period_bytes, buffer_bytes; 459ab4d072SStas Sergeev struct snd_pcm_substream *substream; 469ab4d072SStas Sergeev struct snd_pcm_runtime *runtime; 479ab4d072SStas Sergeev struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer); 48*96c7d478STakashi Iwai unsigned long flags; 499ab4d072SStas Sergeev 509ab4d072SStas Sergeev if (chip->thalf) { 519ab4d072SStas Sergeev outb(chip->val61, 0x61); 529ab4d072SStas Sergeev chip->thalf = 0; 539ab4d072SStas Sergeev if (!atomic_read(&chip->timer_active)) 54*96c7d478STakashi Iwai goto stop; 559ab4d072SStas Sergeev hrtimer_forward(&chip->timer, chip->timer.expires, 569ab4d072SStas Sergeev ktime_set(0, chip->ns_rem)); 579ab4d072SStas Sergeev return HRTIMER_RESTART; 589ab4d072SStas Sergeev } 599ab4d072SStas Sergeev 609ab4d072SStas Sergeev if (!atomic_read(&chip->timer_active)) 61*96c7d478STakashi Iwai goto stop; 62*96c7d478STakashi Iwai substream = chip->playback_substream; 63*96c7d478STakashi Iwai if (!substream) 64*96c7d478STakashi Iwai goto stop; 659ab4d072SStas Sergeev 669ab4d072SStas Sergeev runtime = substream->runtime; 674dfd7954SStas Sergeev fmt_size = snd_pcm_format_physical_width(runtime->format) >> 3; 684dfd7954SStas Sergeev /* assume it is mono! */ 694dfd7954SStas Sergeev val = runtime->dma_area[chip->playback_ptr + fmt_size - 1]; 704dfd7954SStas Sergeev if (snd_pcm_format_signed(runtime->format)) 714dfd7954SStas Sergeev val ^= 0x80; 729ab4d072SStas Sergeev timer_cnt = val * CUR_DIV() / 256; 739ab4d072SStas Sergeev 749ab4d072SStas Sergeev if (timer_cnt && chip->enable) { 759ab4d072SStas Sergeev spin_lock(&i8253_lock); 769ab4d072SStas Sergeev if (!nforce_wa) { 779ab4d072SStas Sergeev outb_p(chip->val61, 0x61); 789ab4d072SStas Sergeev outb_p(timer_cnt, 0x42); 799ab4d072SStas Sergeev outb(chip->val61 ^ 1, 0x61); 809ab4d072SStas Sergeev } else { 819ab4d072SStas Sergeev outb(chip->val61 ^ 2, 0x61); 829ab4d072SStas Sergeev chip->thalf = 1; 839ab4d072SStas Sergeev } 849ab4d072SStas Sergeev spin_unlock(&i8253_lock); 859ab4d072SStas Sergeev } 869ab4d072SStas Sergeev 879ab4d072SStas Sergeev period_bytes = snd_pcm_lib_period_bytes(substream); 889ab4d072SStas Sergeev buffer_bytes = snd_pcm_lib_buffer_bytes(substream); 89*96c7d478STakashi Iwai 90*96c7d478STakashi Iwai spin_lock_irqsave(&chip->substream_lock, flags); 914dfd7954SStas Sergeev chip->playback_ptr += PCSP_INDEX_INC() * fmt_size; 929ab4d072SStas Sergeev periods_elapsed = chip->playback_ptr - chip->period_ptr; 939ab4d072SStas Sergeev if (periods_elapsed < 0) { 9442ece6c1SStas Sergeev #if PCSP_DEBUG 9542ece6c1SStas Sergeev printk(KERN_INFO "PCSP: buffer_bytes mod period_bytes != 0 ? " 969ab4d072SStas Sergeev "(%zi %zi %zi)\n", 979ab4d072SStas Sergeev chip->playback_ptr, period_bytes, buffer_bytes); 9842ece6c1SStas Sergeev #endif 999ab4d072SStas Sergeev periods_elapsed += buffer_bytes; 1009ab4d072SStas Sergeev } 1019ab4d072SStas Sergeev periods_elapsed /= period_bytes; 1029ab4d072SStas Sergeev /* wrap the pointer _before_ calling snd_pcm_period_elapsed(), 1039ab4d072SStas Sergeev * or ALSA will BUG on us. */ 1049ab4d072SStas Sergeev chip->playback_ptr %= buffer_bytes; 1059ab4d072SStas Sergeev 1069ab4d072SStas Sergeev if (periods_elapsed) { 1079ab4d072SStas Sergeev chip->period_ptr += periods_elapsed * period_bytes; 1089ab4d072SStas Sergeev chip->period_ptr %= buffer_bytes; 109*96c7d478STakashi Iwai tasklet_schedule(&pcsp_pcm_tasklet); 1109ab4d072SStas Sergeev } 111*96c7d478STakashi Iwai spin_unlock_irqrestore(&chip->substream_lock, flags); 1129ab4d072SStas Sergeev 1139ab4d072SStas Sergeev if (!atomic_read(&chip->timer_active)) 114*96c7d478STakashi Iwai goto stop; 1159ab4d072SStas Sergeev 1169ab4d072SStas Sergeev chip->ns_rem = PCSP_PERIOD_NS(); 1179ab4d072SStas Sergeev ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem); 1189ab4d072SStas Sergeev chip->ns_rem -= ns; 1199ab4d072SStas Sergeev hrtimer_forward(&chip->timer, chip->timer.expires, ktime_set(0, ns)); 1209ab4d072SStas Sergeev return HRTIMER_RESTART; 1219ab4d072SStas Sergeev 122*96c7d478STakashi Iwai stop: 1239ab4d072SStas Sergeev return HRTIMER_NORESTART; 1249ab4d072SStas Sergeev } 1259ab4d072SStas Sergeev 1269ab4d072SStas Sergeev static void pcsp_start_playing(struct snd_pcsp *chip) 1279ab4d072SStas Sergeev { 1289ab4d072SStas Sergeev #if PCSP_DEBUG 1299ab4d072SStas Sergeev printk(KERN_INFO "PCSP: start_playing called\n"); 1309ab4d072SStas Sergeev #endif 1319ab4d072SStas Sergeev if (atomic_read(&chip->timer_active)) { 1329ab4d072SStas Sergeev printk(KERN_ERR "PCSP: Timer already active\n"); 1339ab4d072SStas Sergeev return; 1349ab4d072SStas Sergeev } 1359ab4d072SStas Sergeev 1369ab4d072SStas Sergeev spin_lock(&i8253_lock); 1379ab4d072SStas Sergeev chip->val61 = inb(0x61) | 0x03; 1389ab4d072SStas Sergeev outb_p(0x92, 0x43); /* binary, mode 1, LSB only, ch 2 */ 1399ab4d072SStas Sergeev spin_unlock(&i8253_lock); 1409ab4d072SStas Sergeev atomic_set(&chip->timer_active, 1); 1419ab4d072SStas Sergeev chip->thalf = 0; 1429ab4d072SStas Sergeev 1434b7afb0dSStas Sergeev hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL); 1449ab4d072SStas Sergeev } 1459ab4d072SStas Sergeev 1469ab4d072SStas Sergeev static void pcsp_stop_playing(struct snd_pcsp *chip) 1479ab4d072SStas Sergeev { 1489ab4d072SStas Sergeev #if PCSP_DEBUG 1499ab4d072SStas Sergeev printk(KERN_INFO "PCSP: stop_playing called\n"); 1509ab4d072SStas Sergeev #endif 1519ab4d072SStas Sergeev if (!atomic_read(&chip->timer_active)) 1529ab4d072SStas Sergeev return; 1539ab4d072SStas Sergeev 1549ab4d072SStas Sergeev atomic_set(&chip->timer_active, 0); 1559ab4d072SStas Sergeev spin_lock(&i8253_lock); 1569ab4d072SStas Sergeev /* restore the timer */ 1579ab4d072SStas Sergeev outb_p(0xb6, 0x43); /* binary, mode 3, LSB/MSB, ch 2 */ 1589ab4d072SStas Sergeev outb(chip->val61 & 0xFC, 0x61); 1599ab4d072SStas Sergeev spin_unlock(&i8253_lock); 1609ab4d072SStas Sergeev } 1619ab4d072SStas Sergeev 162*96c7d478STakashi Iwai /* 163*96c7d478STakashi Iwai * Force to stop and sync the stream 164*96c7d478STakashi Iwai */ 165*96c7d478STakashi Iwai void pcsp_sync_stop(struct snd_pcsp *chip) 166*96c7d478STakashi Iwai { 167*96c7d478STakashi Iwai local_irq_disable(); 168*96c7d478STakashi Iwai pcsp_stop_playing(chip); 169*96c7d478STakashi Iwai local_irq_enable(); 170*96c7d478STakashi Iwai hrtimer_cancel(&chip->timer); 171*96c7d478STakashi Iwai tasklet_kill(&pcsp_pcm_tasklet); 172*96c7d478STakashi Iwai } 173*96c7d478STakashi Iwai 1749ab4d072SStas Sergeev static int snd_pcsp_playback_close(struct snd_pcm_substream *substream) 1759ab4d072SStas Sergeev { 1769ab4d072SStas Sergeev struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 1779ab4d072SStas Sergeev #if PCSP_DEBUG 1789ab4d072SStas Sergeev printk(KERN_INFO "PCSP: close called\n"); 1799ab4d072SStas Sergeev #endif 180*96c7d478STakashi Iwai pcsp_sync_stop(chip); 1819ab4d072SStas Sergeev chip->playback_substream = NULL; 1829ab4d072SStas Sergeev return 0; 1839ab4d072SStas Sergeev } 1849ab4d072SStas Sergeev 1859ab4d072SStas Sergeev static int snd_pcsp_playback_hw_params(struct snd_pcm_substream *substream, 1869ab4d072SStas Sergeev struct snd_pcm_hw_params *hw_params) 1879ab4d072SStas Sergeev { 188*96c7d478STakashi Iwai struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 1899ab4d072SStas Sergeev int err; 190*96c7d478STakashi Iwai pcsp_sync_stop(chip); 1919ab4d072SStas Sergeev err = snd_pcm_lib_malloc_pages(substream, 1929ab4d072SStas Sergeev params_buffer_bytes(hw_params)); 1939ab4d072SStas Sergeev if (err < 0) 1949ab4d072SStas Sergeev return err; 1959ab4d072SStas Sergeev return 0; 1969ab4d072SStas Sergeev } 1979ab4d072SStas Sergeev 1989ab4d072SStas Sergeev static int snd_pcsp_playback_hw_free(struct snd_pcm_substream *substream) 1999ab4d072SStas Sergeev { 200*96c7d478STakashi Iwai struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 2019ab4d072SStas Sergeev #if PCSP_DEBUG 2029ab4d072SStas Sergeev printk(KERN_INFO "PCSP: hw_free called\n"); 2039ab4d072SStas Sergeev #endif 204*96c7d478STakashi Iwai pcsp_sync_stop(chip); 2059ab4d072SStas Sergeev return snd_pcm_lib_free_pages(substream); 2069ab4d072SStas Sergeev } 2079ab4d072SStas Sergeev 2089ab4d072SStas Sergeev static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream) 2099ab4d072SStas Sergeev { 2109ab4d072SStas Sergeev struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 2119ab4d072SStas Sergeev #if PCSP_DEBUG 2129ab4d072SStas Sergeev printk(KERN_INFO "PCSP: prepare called, " 2139ab4d072SStas Sergeev "size=%zi psize=%zi f=%zi f1=%i\n", 2149ab4d072SStas Sergeev snd_pcm_lib_buffer_bytes(substream), 2159ab4d072SStas Sergeev snd_pcm_lib_period_bytes(substream), 2169ab4d072SStas Sergeev snd_pcm_lib_buffer_bytes(substream) / 2179ab4d072SStas Sergeev snd_pcm_lib_period_bytes(substream), 2189ab4d072SStas Sergeev substream->runtime->periods); 2199ab4d072SStas Sergeev #endif 220*96c7d478STakashi Iwai pcsp_sync_stop(chip); 2219ab4d072SStas Sergeev chip->playback_ptr = 0; 2229ab4d072SStas Sergeev chip->period_ptr = 0; 2239ab4d072SStas Sergeev return 0; 2249ab4d072SStas Sergeev } 2259ab4d072SStas Sergeev 2269ab4d072SStas Sergeev static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd) 2279ab4d072SStas Sergeev { 2289ab4d072SStas Sergeev struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 2299ab4d072SStas Sergeev #if PCSP_DEBUG 2309ab4d072SStas Sergeev printk(KERN_INFO "PCSP: trigger called\n"); 2319ab4d072SStas Sergeev #endif 2329ab4d072SStas Sergeev switch (cmd) { 2339ab4d072SStas Sergeev case SNDRV_PCM_TRIGGER_START: 2349ab4d072SStas Sergeev case SNDRV_PCM_TRIGGER_RESUME: 2359ab4d072SStas Sergeev pcsp_start_playing(chip); 2369ab4d072SStas Sergeev break; 2379ab4d072SStas Sergeev case SNDRV_PCM_TRIGGER_STOP: 2389ab4d072SStas Sergeev case SNDRV_PCM_TRIGGER_SUSPEND: 2399ab4d072SStas Sergeev pcsp_stop_playing(chip); 2409ab4d072SStas Sergeev break; 2419ab4d072SStas Sergeev default: 2429ab4d072SStas Sergeev return -EINVAL; 2439ab4d072SStas Sergeev } 2449ab4d072SStas Sergeev return 0; 2459ab4d072SStas Sergeev } 2469ab4d072SStas Sergeev 2479ab4d072SStas Sergeev static snd_pcm_uframes_t snd_pcsp_playback_pointer(struct snd_pcm_substream 2489ab4d072SStas Sergeev *substream) 2499ab4d072SStas Sergeev { 2509ab4d072SStas Sergeev struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 251*96c7d478STakashi Iwai unsigned int pos; 252*96c7d478STakashi Iwai spin_lock(&chip->substream_lock); 253*96c7d478STakashi Iwai pos = chip->playback_ptr; 254*96c7d478STakashi Iwai spin_unlock(&chip->substream_lock); 255*96c7d478STakashi Iwai return bytes_to_frames(substream->runtime, pos); 2569ab4d072SStas Sergeev } 2579ab4d072SStas Sergeev 2589ab4d072SStas Sergeev static struct snd_pcm_hardware snd_pcsp_playback = { 2599ab4d072SStas Sergeev .info = (SNDRV_PCM_INFO_INTERLEAVED | 2609ab4d072SStas Sergeev SNDRV_PCM_INFO_HALF_DUPLEX | 2619ab4d072SStas Sergeev SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), 2624dfd7954SStas Sergeev .formats = (SNDRV_PCM_FMTBIT_U8 2634dfd7954SStas Sergeev #if DMIX_WANTS_S16 2644dfd7954SStas Sergeev | SNDRV_PCM_FMTBIT_S16_LE 2654dfd7954SStas Sergeev #endif 2664dfd7954SStas Sergeev ), 2679ab4d072SStas Sergeev .rates = SNDRV_PCM_RATE_KNOT, 2689ab4d072SStas Sergeev .rate_min = PCSP_DEFAULT_SRATE, 2699ab4d072SStas Sergeev .rate_max = PCSP_DEFAULT_SRATE, 2709ab4d072SStas Sergeev .channels_min = 1, 2719ab4d072SStas Sergeev .channels_max = 1, 2729ab4d072SStas Sergeev .buffer_bytes_max = PCSP_BUFFER_SIZE, 2739ab4d072SStas Sergeev .period_bytes_min = 64, 2749ab4d072SStas Sergeev .period_bytes_max = PCSP_MAX_PERIOD_SIZE, 2759ab4d072SStas Sergeev .periods_min = 2, 2769ab4d072SStas Sergeev .periods_max = PCSP_MAX_PERIODS, 2779ab4d072SStas Sergeev .fifo_size = 0, 2789ab4d072SStas Sergeev }; 2799ab4d072SStas Sergeev 2809ab4d072SStas Sergeev static int snd_pcsp_playback_open(struct snd_pcm_substream *substream) 2819ab4d072SStas Sergeev { 2829ab4d072SStas Sergeev struct snd_pcsp *chip = snd_pcm_substream_chip(substream); 2839ab4d072SStas Sergeev struct snd_pcm_runtime *runtime = substream->runtime; 2849ab4d072SStas Sergeev #if PCSP_DEBUG 2859ab4d072SStas Sergeev printk(KERN_INFO "PCSP: open called\n"); 2869ab4d072SStas Sergeev #endif 2879ab4d072SStas Sergeev if (atomic_read(&chip->timer_active)) { 2889ab4d072SStas Sergeev printk(KERN_ERR "PCSP: still active!!\n"); 2899ab4d072SStas Sergeev return -EBUSY; 2909ab4d072SStas Sergeev } 2919ab4d072SStas Sergeev runtime->hw = snd_pcsp_playback; 2929ab4d072SStas Sergeev chip->playback_substream = substream; 2939ab4d072SStas Sergeev return 0; 2949ab4d072SStas Sergeev } 2959ab4d072SStas Sergeev 2969ab4d072SStas Sergeev static struct snd_pcm_ops snd_pcsp_playback_ops = { 2979ab4d072SStas Sergeev .open = snd_pcsp_playback_open, 2989ab4d072SStas Sergeev .close = snd_pcsp_playback_close, 2999ab4d072SStas Sergeev .ioctl = snd_pcm_lib_ioctl, 3009ab4d072SStas Sergeev .hw_params = snd_pcsp_playback_hw_params, 3019ab4d072SStas Sergeev .hw_free = snd_pcsp_playback_hw_free, 3029ab4d072SStas Sergeev .prepare = snd_pcsp_playback_prepare, 3039ab4d072SStas Sergeev .trigger = snd_pcsp_trigger, 3049ab4d072SStas Sergeev .pointer = snd_pcsp_playback_pointer, 3059ab4d072SStas Sergeev }; 3069ab4d072SStas Sergeev 3079ab4d072SStas Sergeev int __devinit snd_pcsp_new_pcm(struct snd_pcsp *chip) 3089ab4d072SStas Sergeev { 3099ab4d072SStas Sergeev int err; 3109ab4d072SStas Sergeev 3119ab4d072SStas Sergeev err = snd_pcm_new(chip->card, "pcspeaker", 0, 1, 0, &chip->pcm); 3129ab4d072SStas Sergeev if (err < 0) 3139ab4d072SStas Sergeev return err; 3149ab4d072SStas Sergeev 3159ab4d072SStas Sergeev snd_pcm_set_ops(chip->pcm, SNDRV_PCM_STREAM_PLAYBACK, 3169ab4d072SStas Sergeev &snd_pcsp_playback_ops); 3179ab4d072SStas Sergeev 3189ab4d072SStas Sergeev chip->pcm->private_data = chip; 3199ab4d072SStas Sergeev chip->pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; 3209ab4d072SStas Sergeev strcpy(chip->pcm->name, "pcsp"); 3219ab4d072SStas Sergeev 3229ab4d072SStas Sergeev snd_pcm_lib_preallocate_pages_for_all(chip->pcm, 3239ab4d072SStas Sergeev SNDRV_DMA_TYPE_CONTINUOUS, 3249ab4d072SStas Sergeev snd_dma_continuous_data 3259ab4d072SStas Sergeev (GFP_KERNEL), PCSP_BUFFER_SIZE, 3269ab4d072SStas Sergeev PCSP_BUFFER_SIZE); 3279ab4d072SStas Sergeev 3289ab4d072SStas Sergeev return 0; 3299ab4d072SStas Sergeev } 330