Commit f240406b authored by Jaroslav Kysela's avatar Jaroslav Kysela

ALSA: pcm_lib - cleanup & merge hw_ptr update functions

Do general cleanup in snd_pcm_update_hw_ptr*() routines and merge them.
The main change is hw_ptr_interrupt variable removal to simplify code
logic. This variable can be computed directly from hw_ptr.

Ensure that updated hw_ptr is not lower than previous one (it was possible
with old code in some obscure situations when interrupt was delayed or
the lowlevel driver returns wrong ring buffer position value).
Signed-off-by: default avatarJaroslav Kysela <perex@perex.cz>
parent 4d96eb25
...@@ -271,7 +271,6 @@ struct snd_pcm_runtime { ...@@ -271,7 +271,6 @@ struct snd_pcm_runtime {
int overrange; int overrange;
snd_pcm_uframes_t avail_max; snd_pcm_uframes_t avail_max;
snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */
snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */
unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */ unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */
snd_pcm_sframes_t delay; /* extra delay; typically FIFO size */ snd_pcm_sframes_t delay; /* extra delay; typically FIFO size */
......
...@@ -61,7 +61,7 @@ struct snd_pcm_oss_runtime { ...@@ -61,7 +61,7 @@ struct snd_pcm_oss_runtime {
struct snd_pcm_plugin *plugin_first; struct snd_pcm_plugin *plugin_first;
struct snd_pcm_plugin *plugin_last; struct snd_pcm_plugin *plugin_last;
#endif #endif
unsigned int prev_hw_ptr_interrupt; unsigned int prev_hw_ptr_period;
}; };
struct snd_pcm_oss_file { struct snd_pcm_oss_file {
......
...@@ -632,6 +632,13 @@ static long snd_pcm_alsa_frames(struct snd_pcm_substream *substream, long bytes) ...@@ -632,6 +632,13 @@ static long snd_pcm_alsa_frames(struct snd_pcm_substream *substream, long bytes)
return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes); return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes);
} }
static inline
snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime)
{
snd_pcm_uframes_t ptr = runtime->status->hw_ptr;
return ptr - (ptr % runtime->period_size);
}
/* define extended formats in the recent OSS versions (if any) */ /* define extended formats in the recent OSS versions (if any) */
/* linear formats */ /* linear formats */
#define AFMT_S32_LE 0x00001000 #define AFMT_S32_LE 0x00001000
...@@ -1102,7 +1109,7 @@ static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream) ...@@ -1102,7 +1109,7 @@ static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
return err; return err;
} }
runtime->oss.prepare = 0; runtime->oss.prepare = 0;
runtime->oss.prev_hw_ptr_interrupt = 0; runtime->oss.prev_hw_ptr_period = 0;
runtime->oss.period_ptr = 0; runtime->oss.period_ptr = 0;
runtime->oss.buffer_used = 0; runtime->oss.buffer_used = 0;
...@@ -1950,7 +1957,8 @@ static int snd_pcm_oss_get_caps(struct snd_pcm_oss_file *pcm_oss_file) ...@@ -1950,7 +1957,8 @@ static int snd_pcm_oss_get_caps(struct snd_pcm_oss_file *pcm_oss_file)
return result; return result;
} }
static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream, snd_pcm_uframes_t hw_ptr) static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream,
snd_pcm_uframes_t hw_ptr)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t appl_ptr; snd_pcm_uframes_t appl_ptr;
...@@ -1986,7 +1994,8 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr ...@@ -1986,7 +1994,8 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
if (runtime->oss.trigger) if (runtime->oss.trigger)
goto _skip1; goto _skip1;
if (atomic_read(&psubstream->mmap_count)) if (atomic_read(&psubstream->mmap_count))
snd_pcm_oss_simulate_fill(psubstream, runtime->hw_ptr_interrupt); snd_pcm_oss_simulate_fill(psubstream,
get_hw_ptr_period(runtime));
runtime->oss.trigger = 1; runtime->oss.trigger = 1;
runtime->start_threshold = 1; runtime->start_threshold = 1;
cmd = SNDRV_PCM_IOCTL_START; cmd = SNDRV_PCM_IOCTL_START;
...@@ -2105,11 +2114,12 @@ static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream ...@@ -2105,11 +2114,12 @@ static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream
info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size); info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size);
if (atomic_read(&substream->mmap_count)) { if (atomic_read(&substream->mmap_count)) {
snd_pcm_sframes_t n; snd_pcm_sframes_t n;
n = (delay = runtime->hw_ptr_interrupt) - runtime->oss.prev_hw_ptr_interrupt; delay = get_hw_ptr_period(runtime);
n = delay - runtime->oss.prev_hw_ptr_period;
if (n < 0) if (n < 0)
n += runtime->boundary; n += runtime->boundary;
info.blocks = n / runtime->period_size; info.blocks = n / runtime->period_size;
runtime->oss.prev_hw_ptr_interrupt = delay; runtime->oss.prev_hw_ptr_period = delay;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_pcm_oss_simulate_fill(substream, delay); snd_pcm_oss_simulate_fill(substream, delay);
info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX; info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX;
...@@ -2673,18 +2683,22 @@ static int snd_pcm_oss_playback_ready(struct snd_pcm_substream *substream) ...@@ -2673,18 +2683,22 @@ static int snd_pcm_oss_playback_ready(struct snd_pcm_substream *substream)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
if (atomic_read(&substream->mmap_count)) if (atomic_read(&substream->mmap_count))
return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; return runtime->oss.prev_hw_ptr_period !=
get_hw_ptr_period(runtime);
else else
return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames; return snd_pcm_playback_avail(runtime) >=
runtime->oss.period_frames;
} }
static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream) static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
if (atomic_read(&substream->mmap_count)) if (atomic_read(&substream->mmap_count))
return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; return runtime->oss.prev_hw_ptr_period !=
get_hw_ptr_period(runtime);
else else
return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames; return snd_pcm_capture_avail(runtime) >=
runtime->oss.period_frames;
} }
static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait) static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
......
This diff is collapsed.
...@@ -1247,8 +1247,6 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state) ...@@ -1247,8 +1247,6 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state)
if (err < 0) if (err < 0)
return err; return err;
runtime->hw_ptr_base = 0; runtime->hw_ptr_base = 0;
runtime->hw_ptr_interrupt = runtime->status->hw_ptr -
runtime->status->hw_ptr % runtime->period_size;
runtime->silence_start = runtime->status->hw_ptr; runtime->silence_start = runtime->status->hw_ptr;
runtime->silence_filled = 0; runtime->silence_filled = 0;
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment