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