ALSA: pcsp - Fix starting the stream with HRTIMER_CB_IRQSAFE_UNLOCK
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 <tiwai@suse.de>
This commit is contained in:
parent
e7dd8c1bda
commit
eea0579fc8
2 changed files with 58 additions and 31 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
hrtimer_forward(handle, hrtimer_get_expires(handle), ns_to_ktime(ns));
|
||||
|
||||
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;
|
||||
|
||||
stop:
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue