Skip to content

Commit

Permalink
ALSA: pcsp - Fix starting the stream with HRTIMER_CB_IRQSAFE_UNLOCK
Browse files Browse the repository at this point in the history
With the callback mode HRTIMER_CB_IRQSAFE_UNLOCK, the start of the
stream with zero delay doesn't work.  Since IRQSAFE mode is removed,
we have to change the pcsp start-up code.

This patch splits the callback function to two parts, the triggering
of the port and the calculation of the expire time, and the update of
the ALSA PCM core.  The first part is called both from the trigger-start
and the hrtimer callback while the latter is handled only in the
hrtimer callback.

Signed-off-by: Takashi Iwai <[email protected]>
  • Loading branch information
tiwai committed Nov 26, 2008
1 parent e7dd8c1 commit eea0579
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 31 deletions.
2 changes: 2 additions & 0 deletions sound/drivers/pcsp/pcsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ struct snd_pcsp {
unsigned short port, irq, dma;
spinlock_t substream_lock;
struct snd_pcm_substream *playback_substream;
unsigned int fmt_size;
unsigned int is_signed;
size_t playback_ptr;
size_t period_ptr;
atomic_t timer_active;
Expand Down
87 changes: 56 additions & 31 deletions sound/drivers/pcsp/pcsp_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ static void pcsp_call_pcm_elapsed(unsigned long priv)

static DECLARE_TASKLET(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed, 0);

enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
/* write the port and returns the next expire time in ns;
* called at the trigger-start and in hrtimer callback
*/
static unsigned long pcsp_timer_update(struct hrtimer *handle)
{
unsigned char timer_cnt, val;
int fmt_size, periods_elapsed;
u64 ns;
size_t period_bytes, buffer_bytes;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
struct snd_pcsp *chip = container_of(handle, struct snd_pcsp, timer);
Expand All @@ -51,28 +52,25 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
outb(chip->val61, 0x61);
chip->thalf = 0;
if (!atomic_read(&chip->timer_active))
goto stop;
hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer),
ktime_set(0, chip->ns_rem));
return HRTIMER_RESTART;
return 0;
return chip->ns_rem;
}

if (!atomic_read(&chip->timer_active))
goto stop;
return 0;
substream = chip->playback_substream;
if (!substream)
goto stop;
return 0;

runtime = substream->runtime;
fmt_size = snd_pcm_format_physical_width(runtime->format) >> 3;
/* assume it is mono! */
val = runtime->dma_area[chip->playback_ptr + fmt_size - 1];
if (snd_pcm_format_signed(runtime->format))
val = runtime->dma_area[chip->playback_ptr + chip->fmt_size - 1];
if (chip->is_signed)
val ^= 0x80;
timer_cnt = val * CUR_DIV() / 256;

if (timer_cnt && chip->enable) {
spin_lock(&i8253_lock);
spin_lock_irqsave(&i8253_lock, flags);
if (!nforce_wa) {
outb_p(chip->val61, 0x61);
outb_p(timer_cnt, 0x42);
Expand All @@ -81,14 +79,39 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
outb(chip->val61 ^ 2, 0x61);
chip->thalf = 1;
}
spin_unlock(&i8253_lock);
spin_unlock_irqrestore(&i8253_lock, flags);
}

chip->ns_rem = PCSP_PERIOD_NS();
ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
chip->ns_rem -= ns;
return ns;
}

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() * fmt_size;
chip->playback_ptr += PCSP_INDEX_INC() * chip->fmt_size;
periods_elapsed = chip->playback_ptr - chip->period_ptr;
if (periods_elapsed < 0) {
#if PCSP_DEBUG
Expand All @@ -106,32 +129,27 @@ enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle)
if (periods_elapsed) {
chip->period_ptr += periods_elapsed * period_bytes;
chip->period_ptr %= buffer_bytes;
tasklet_schedule(&pcsp_pcm_tasklet);
}
spin_unlock_irqrestore(&chip->substream_lock, flags);

if (!atomic_read(&chip->timer_active))
goto stop;
if (periods_elapsed)
tasklet_schedule(&pcsp_pcm_tasklet);

chip->ns_rem = PCSP_PERIOD_NS();
ns = (chip->thalf ? PCSP_CALC_NS(timer_cnt) : chip->ns_rem);
chip->ns_rem -= ns;
hrtimer_forward(&chip->timer, hrtimer_get_expires(&chip->timer),
ktime_set(0, ns));
return HRTIMER_RESTART;
hrtimer_forward(handle, hrtimer_get_expires(handle), ns_to_ktime(ns));

stop:
return HRTIMER_NORESTART;
return HRTIMER_RESTART;
}

static void pcsp_start_playing(struct snd_pcsp *chip)
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;
return -EIO;
}

spin_lock(&i8253_lock);
Expand All @@ -141,7 +159,12 @@ static void pcsp_start_playing(struct snd_pcsp *chip)
atomic_set(&chip->timer_active, 1);
chip->thalf = 0;

hrtimer_start(&pcsp_chip.timer, ktime_set(0, 0), HRTIMER_MODE_REL);
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;
}

static void pcsp_stop_playing(struct snd_pcsp *chip)
Expand Down Expand Up @@ -221,6 +244,9 @@ static int snd_pcsp_playback_prepare(struct snd_pcm_substream *substream)
pcsp_sync_stop(chip);
chip->playback_ptr = 0;
chip->period_ptr = 0;
chip->fmt_size =
snd_pcm_format_physical_width(substream->runtime->format) >> 3;
chip->is_signed = snd_pcm_format_signed(substream->runtime->format);
return 0;
}

Expand All @@ -233,8 +259,7 @@ static int snd_pcsp_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
pcsp_start_playing(chip);
break;
return pcsp_start_playing(chip);
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
pcsp_stop_playing(chip);
Expand Down

0 comments on commit eea0579

Please sign in to comment.