Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 143368
b: refs/heads/master
c: 2e8e59f
h: refs/heads/master
v: v3
  • Loading branch information
Takashi Iwai committed Apr 15, 2009
1 parent 3966ce3 commit 078dc14
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 45 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: ff0c68d03b77ecb3a682137c42e947a4dc33e9b3
refs/heads/master: 2e8e59f437ce303fde2077cc5f84ce03c6ff1020
3 changes: 2 additions & 1 deletion trunk/include/sound/pcm.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,8 @@ struct snd_pcm_runtime {
int overrange;
snd_pcm_uframes_t avail_max;
snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */
snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/
snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */
unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */

/* -- HW params -- */
snd_pcm_access_t access; /* access mode */
Expand Down
47 changes: 39 additions & 8 deletions trunk/sound/core/pcm_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,11 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t pos;
snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt, hw_base;
snd_pcm_sframes_t delta;
snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base;
snd_pcm_sframes_t hdelta, delta;
unsigned long jdelta;

old_hw_ptr = runtime->status->hw_ptr;
pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
if (pos == SNDRV_PCM_POS_XRUN) {
xrun(substream);
Expand Down Expand Up @@ -247,7 +249,30 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
new_hw_ptr = hw_base + pos;
}
}
if (delta > runtime->period_size) {
hdelta = new_hw_ptr - old_hw_ptr;
jdelta = jiffies - runtime->hw_ptr_jiffies;
if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
delta = jdelta /
(((runtime->period_size * HZ) / runtime->rate)
+ HZ/100);
hw_ptr_error(substream,
"hw_ptr skipping! [Q] "
"(pos=%ld, delta=%ld, period=%ld, "
"jdelta=%lu/%lu/%lu)\n",
(long)pos, (long)hdelta,
(long)runtime->period_size, jdelta,
((hdelta * HZ) / runtime->rate), delta);
hw_ptr_interrupt = runtime->hw_ptr_interrupt +
runtime->period_size * delta;
if (hw_ptr_interrupt >= runtime->boundary)
hw_ptr_interrupt -= runtime->boundary;
/* rebase to interrupt position */
hw_base = new_hw_ptr = hw_ptr_interrupt;
/* align hw_base to buffer_size */
hw_base -= hw_base % runtime->buffer_size;
delta = 0;
}
if (delta > runtime->period_size + runtime->period_size / 2) {
hw_ptr_error(substream,
"Lost interrupts? "
"(stream=%i, delta=%ld, intr_ptr=%ld)\n",
Expand All @@ -263,6 +288,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)

runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr;
runtime->hw_ptr_jiffies = jiffies;
runtime->hw_ptr_interrupt = hw_ptr_interrupt;

return snd_pcm_update_hw_ptr_post(substream, runtime);
Expand All @@ -275,6 +301,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
snd_pcm_uframes_t pos;
snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
snd_pcm_sframes_t delta;
unsigned long jdelta;

old_hw_ptr = runtime->status->hw_ptr;
pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
Expand All @@ -286,27 +313,29 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
new_hw_ptr = hw_base + pos;

delta = new_hw_ptr - old_hw_ptr;
jdelta = jiffies - runtime->hw_ptr_jiffies;
if (delta < 0) {
delta += runtime->buffer_size;
if (delta < 0) {
hw_ptr_error(substream,
"Unexpected hw_pointer value [2] "
"(stream=%i, pos=%ld, old_ptr=%ld)\n",
"(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n",
substream->stream, (long)pos,
(long)old_hw_ptr);
(long)old_hw_ptr, jdelta);
return 0;
}
hw_base += runtime->buffer_size;
if (hw_base >= runtime->boundary)
hw_base = 0;
new_hw_ptr = hw_base + pos;
}
if (delta > runtime->period_size && runtime->periods > 1) {
if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) {
hw_ptr_error(substream,
"hw_ptr skipping! "
"(pos=%ld, delta=%ld, period=%ld)\n",
"(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n",
(long)pos, (long)delta,
(long)runtime->period_size);
(long)runtime->period_size, jdelta,
((delta * HZ) / runtime->rate));
return 0;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
Expand All @@ -315,6 +344,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)

runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr;
runtime->hw_ptr_jiffies = jiffies;

return snd_pcm_update_hw_ptr_post(substream, runtime);
}
Expand Down Expand Up @@ -1441,6 +1471,7 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
runtime->status->hw_ptr %= runtime->buffer_size;
else
runtime->status->hw_ptr = 0;
runtime->hw_ptr_jiffies = jiffies;
snd_pcm_stream_unlock_irqrestore(substream, flags);
return 0;
}
Expand Down
39 changes: 25 additions & 14 deletions trunk/sound/pci/hda/hda_intel.c
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,9 @@ struct azx_dev {
unsigned int period_bytes; /* size of the period in bytes */
unsigned int frags; /* number for period in the play buffer */
unsigned int fifo_size; /* FIFO size */
unsigned int start_flag: 1; /* stream full start flag */
unsigned long start_jiffies; /* start + minimum jiffies */
unsigned long min_jiffies; /* minimum jiffies before position is valid */

void __iomem *sd_addr; /* stream descriptor pointer */

Expand All @@ -330,7 +333,6 @@ struct azx_dev {
unsigned int opened :1;
unsigned int running :1;
unsigned int irq_pending :1;
unsigned int irq_ignore :1;
/*
* For VIA:
* A flag to ensure DMA position is 0
Expand Down Expand Up @@ -975,7 +977,7 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
struct azx *chip = dev_id;
struct azx_dev *azx_dev;
u32 status;
int i;
int i, ok;

spin_lock(&chip->reg_lock);

Expand All @@ -991,18 +993,14 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
if (!azx_dev->substream || !azx_dev->running)
continue;
/* ignore the first dummy IRQ (due to pos_adj) */
if (azx_dev->irq_ignore) {
azx_dev->irq_ignore = 0;
continue;
}
/* check whether this IRQ is really acceptable */
if (azx_position_ok(chip, azx_dev)) {
ok = azx_position_ok(chip, azx_dev);
if (ok == 1) {
azx_dev->irq_pending = 0;
spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(azx_dev->substream);
spin_lock(&chip->reg_lock);
} else if (chip->bus && chip->bus->workq) {
} else if (ok == 0 && chip->bus && chip->bus->workq) {
/* bogus IRQ, process it later */
azx_dev->irq_pending = 1;
queue_work(chip->bus->workq,
Expand Down Expand Up @@ -1088,7 +1086,6 @@ static int azx_setup_periods(struct azx *chip,
bdl = (u32 *)azx_dev->bdl.area;
ofs = 0;
azx_dev->frags = 0;
azx_dev->irq_ignore = 0;
pos_adj = bdl_pos_adj[chip->dev_index];
if (pos_adj > 0) {
struct snd_pcm_runtime *runtime = substream->runtime;
Expand All @@ -1109,7 +1106,6 @@ static int azx_setup_periods(struct azx *chip,
&bdl, ofs, pos_adj, 1);
if (ofs < 0)
goto error;
azx_dev->irq_ignore = 1;
}
} else
pos_adj = 0;
Expand Down Expand Up @@ -1155,6 +1151,9 @@ static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev)
while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&
--timeout)
;

/* reset first position - may not be synced with hw at this time */
*azx_dev->posbuf = 0;
}

/*
Expand Down Expand Up @@ -1409,7 +1408,6 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
snd_pcm_set_sync(substream);
mutex_unlock(&chip->open_mutex);

azx_stream_reset(chip, azx_dev);
return 0;
}

Expand Down Expand Up @@ -1474,6 +1472,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
unsigned int bufsize, period_bytes, format_val;
int err;

azx_stream_reset(chip, azx_dev);
format_val = snd_hda_calc_stream_format(runtime->rate,
runtime->channels,
runtime->format,
Expand Down Expand Up @@ -1502,6 +1501,8 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
return err;
}

azx_dev->min_jiffies = (runtime->period_size * HZ) /
(runtime->rate * 2);
azx_setup_controller(chip, azx_dev);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
Expand All @@ -1518,13 +1519,14 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct azx *chip = apcm->chip;
struct azx_dev *azx_dev;
struct snd_pcm_substream *s;
int start, nsync = 0, sbits = 0;
int rstart = 0, start, nsync = 0, sbits = 0;
int nwait, timeout;

switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
rstart = 1;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_START:
start = 1;
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
Expand Down Expand Up @@ -1554,6 +1556,10 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
if (s->pcm->card != substream->pcm->card)
continue;
azx_dev = get_azx_dev(s);
if (rstart) {
azx_dev->start_flag = 1;
azx_dev->start_jiffies = jiffies + azx_dev->min_jiffies;
}
if (start)
azx_stream_start(chip, azx_dev);
else
Expand Down Expand Up @@ -1703,6 +1709,11 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
{
unsigned int pos;

if (azx_dev->start_flag &&
time_before_eq(jiffies, azx_dev->start_jiffies))
return -1; /* bogus (too early) interrupt */
azx_dev->start_flag = 0;

pos = azx_get_position(chip, azx_dev);
if (chip->position_fix == POS_FIX_AUTO) {
if (!pos) {
Expand Down
Loading

0 comments on commit 078dc14

Please sign in to comment.