static int pcsp_start_playing(struct snd_pcsp *chip) { unsigned long ns; #if PCSP_DEBUG printk(KERN_INFO "PCSP: start_playing called\n"); #endif if (atomic_read(&chip->timer_active)) { printk(KERN_ERR "PCSP: Timer already active\n"); return -EIO; } spin_lock(&i8253_lock); chip->val61 = inb(0x61) | 0x03; outb_p(0x92, 0x43); /* binary, mode 1, LSB only, ch 2 */ spin_unlock(&i8253_lock); atomic_set(&chip->timer_active, 1); chip->thalf = 0; ns = pcsp_timer_update(&pcsp_chip.timer); if (!ns) return -EIO; hrtimer_start(&pcsp_chip.timer, ktime_set(0, ns), HRTIMER_MODE_REL); return 0; }
enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) { struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer); struct snd_pcm_substream *substream; int periods_elapsed, pointer_update; size_t period_bytes, buffer_bytes; unsigned long ns; unsigned long flags; pointer_update = !chip->thalf; ns = pcsp_timer_update(handle); if (!ns) return HRTIMER_NORESTART; /* update the playback position */ substream = chip->playback_substream; if (!substream) return HRTIMER_NORESTART; period_bytes = snd_pcm_lib_period_bytes(substream); buffer_bytes = snd_pcm_lib_buffer_bytes(substream); spin_lock_irqsave(&chip->substream_lock, flags); chip->playback_ptr += PCSP_INDEX_INC() * chip->fmt_size; periods_elapsed = chip->playback_ptr - chip->period_ptr; if (periods_elapsed < 0) { #if PCSP_DEBUG printk(KERN_INFO "PCSP: buffer_bytes mod period_bytes != 0 ? " "(%zi %zi %zi)\n", chip->playback_ptr, period_bytes, buffer_bytes); #endif periods_elapsed += buffer_bytes; } periods_elapsed /= period_bytes; /* wrap the pointer _before_ calling snd_pcm_period_elapsed(), * or ALSA will BUG on us. */ chip->playback_ptr %= buffer_bytes; if (periods_elapsed) { chip->period_ptr += periods_elapsed * period_bytes; chip->period_ptr %= buffer_bytes; } spin_unlock_irqrestore(&chip->substream_lock, flags); if (periods_elapsed) tasklet_schedule(&pcsp_pcm_tasklet); hrtimer_forward(handle, hrtimer_get_expires(handle), ns_to_ktime(ns)); return HRTIMER_RESTART; }
enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) { struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer); int pointer_update; u64 ns; if (!atomic_read(&chip->timer_active) || !chip->playback_substream) return HRTIMER_NORESTART; pointer_update = !chip->thalf; ns = pcsp_timer_update(chip); if (!ns) { printk(KERN_WARNING "PCSP: unexpected stop\n"); return HRTIMER_NORESTART; } if (pointer_update) pcsp_pointer_update(chip); hrtimer_forward(handle, hrtimer_get_expires(handle), ns_to_ktime(ns)); return HRTIMER_RESTART; }