aboutsummaryrefslogtreecommitdiff
path: root/sound/core/pcm_lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/core/pcm_lib.c')
-rw-r--r--sound/core/pcm_lib.c142
1 files changed, 110 insertions, 32 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index f42c10a4331..9acc77eae48 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -174,7 +174,7 @@ static void xrun(struct snd_pcm_substream *substream)
if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
char name[16];
snd_pcm_debug_name(substream, name, sizeof(name));
- snd_printd(KERN_DEBUG "XRUN: %s\n", name);
+ pcm_warn(substream->pcm, "XRUN: %s\n", name);
dump_stack_on_xrun(substream);
}
}
@@ -184,9 +184,7 @@ static void xrun(struct snd_pcm_substream *substream)
do { \
if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \
xrun_log_show(substream); \
- if (printk_ratelimit()) { \
- snd_printd("PCM: " fmt, ##args); \
- } \
+ pr_err_ratelimited("ALSA: PCM: " fmt, ##args); \
dump_stack_on_xrun(substream); \
} \
} while (0)
@@ -253,7 +251,7 @@ static void xrun_log_show(struct snd_pcm_substream *substream)
entry = &log->entries[idx];
if (entry->period_size == 0)
break;
- snd_printd("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, "
+ pr_info("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, "
"hwptr=%ld/%ld\n",
name, entry->in_interrupt ? "[Q] " : "",
entry->jiffies,
@@ -316,6 +314,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
unsigned long jdelta;
unsigned long curr_jiffies;
struct timespec curr_tstamp;
+ struct timespec audio_tstamp;
+ int crossed_boundary = 0;
old_hw_ptr = runtime->status->hw_ptr;
@@ -327,9 +327,14 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
*/
pos = substream->ops->pointer(substream);
curr_jiffies = jiffies;
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
+ if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) &&
+ (substream->ops->wall_clock))
+ substream->ops->wall_clock(substream, &audio_tstamp);
+ }
+
if (pos == SNDRV_PCM_POS_XRUN) {
xrun(substream);
return -EPIPE;
@@ -339,10 +344,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
char name[16];
snd_pcm_debug_name(substream, name, sizeof(name));
xrun_log_show(substream);
- snd_printd(KERN_ERR "BUG: %s, pos = %ld, "
- "buffer size = %ld, period size = %ld\n",
- name, pos, runtime->buffer_size,
- runtime->period_size);
+ pcm_err(substream->pcm,
+ "XRUN: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
+ name, pos, runtime->buffer_size,
+ runtime->period_size);
}
pos = 0;
}
@@ -360,8 +365,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
hdelta = curr_jiffies - runtime->hw_ptr_jiffies;
if (hdelta > runtime->hw_ptr_buffer_jiffies/2) {
hw_base += runtime->buffer_size;
- if (hw_base >= runtime->boundary)
+ if (hw_base >= runtime->boundary) {
hw_base = 0;
+ crossed_boundary++;
+ }
new_hw_ptr = hw_base + pos;
goto __delta;
}
@@ -371,8 +378,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
/* pointer crosses the end of the ring buffer */
if (new_hw_ptr < old_hw_ptr) {
hw_base += runtime->buffer_size;
- if (hw_base >= runtime->boundary)
+ if (hw_base >= runtime->boundary) {
hw_base = 0;
+ crossed_boundary++;
+ }
new_hw_ptr = hw_base + pos;
}
__delta:
@@ -383,8 +392,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
char name[16];
snd_pcm_debug_name(substream, name, sizeof(name));
- snd_printd("%s_update: %s: pos=%u/%u/%u, "
- "hwptr=%ld/%ld/%ld/%ld\n",
+ pcm_dbg(substream->pcm,
+ "%s_update: %s: pos=%u/%u/%u, hwptr=%ld/%ld/%ld/%ld\n",
in_interrupt ? "period" : "hwptr",
name,
(unsigned int)pos,
@@ -410,8 +419,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
while (hdelta > xrun_threshold) {
delta += runtime->buffer_size;
hw_base += runtime->buffer_size;
- if (hw_base >= runtime->boundary)
+ if (hw_base >= runtime->boundary) {
hw_base = 0;
+ crossed_boundary++;
+ }
new_hw_ptr = hw_base + pos;
hdelta -= runtime->hw_ptr_buffer_jiffies;
}
@@ -456,8 +467,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
/* the delta value is small or zero in most cases */
while (delta > 0) {
new_hw_ptr += runtime->period_size;
- if (new_hw_ptr >= runtime->boundary)
+ if (new_hw_ptr >= runtime->boundary) {
new_hw_ptr -= runtime->boundary;
+ crossed_boundary--;
+ }
delta--;
}
/* align hw_base to buffer_size */
@@ -507,9 +520,35 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr;
runtime->hw_ptr_jiffies = curr_jiffies;
- if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+ if (crossed_boundary) {
+ snd_BUG_ON(crossed_boundary != 1);
+ runtime->hw_ptr_wrap += runtime->boundary;
+ }
+ if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
runtime->status->tstamp = curr_tstamp;
+ if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) {
+ /*
+ * no wall clock available, provide audio timestamp
+ * derived from pointer position+delay
+ */
+ u64 audio_frames, audio_nsecs;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ audio_frames = runtime->hw_ptr_wrap
+ + runtime->status->hw_ptr
+ - runtime->delay;
+ else
+ audio_frames = runtime->hw_ptr_wrap
+ + runtime->status->hw_ptr
+ + runtime->delay;
+ audio_nsecs = div_u64(audio_frames * 1000000000LL,
+ runtime->rate);
+ audio_tstamp = ns_to_timespec(audio_nsecs);
+ }
+ runtime->status->audio_tstamp = audio_tstamp;
+ }
+
return snd_pcm_update_state(substream, runtime);
}
@@ -527,7 +566,8 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
*
* Sets the given PCM operators to the pcm instance.
*/
-void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops)
+void snd_pcm_set_ops(struct snd_pcm *pcm, int direction,
+ const struct snd_pcm_ops *ops)
{
struct snd_pcm_str *stream = &pcm->streams[direction];
struct snd_pcm_substream *substream;
@@ -625,7 +665,8 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,
* The interval is changed to the range satisfying both intervals.
* The interval status (min, max, integer, etc.) are evaluated.
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)
{
@@ -824,7 +865,8 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
* @nump: pointer to store the resultant numerator
* @denp: pointer to store the resultant denominator
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_interval_ratnum(struct snd_interval *i,
unsigned int rats_count, struct snd_ratnum *rats,
@@ -942,7 +984,8 @@ EXPORT_SYMBOL(snd_interval_ratnum);
* @nump: pointer to store the resultant numerator
* @denp: pointer to store the resultant denominator
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
static int snd_interval_ratden(struct snd_interval *i,
unsigned int rats_count, struct snd_ratden *rats,
@@ -1041,7 +1084,8 @@ static int snd_interval_ratden(struct snd_interval *i,
* When mask is non-zero, only the elements corresponding to bit 1 are
* evaluated.
*
- * Returns non-zero if the value is changed, zero if not changed.
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_interval_list(struct snd_interval *i, unsigned int count,
const unsigned int *list, unsigned int mask)
@@ -1101,7 +1145,7 @@ static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned
* @private: the private data pointer passed to function
* @dep: the dependent variables
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,
int var,
@@ -1159,6 +1203,8 @@ EXPORT_SYMBOL(snd_pcm_hw_rule_add);
* @mask: the bitmap mask
*
* Apply the constraint of the given bitmap mask to a 32-bit mask parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
u_int32_t mask)
@@ -1179,6 +1225,8 @@ int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param
* @mask: the 64bit bitmap mask
*
* Apply the constraint of the given bitmap mask to a 64-bit mask parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
u_int64_t mask)
@@ -1192,6 +1240,7 @@ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_par
return -EINVAL;
return 0;
}
+EXPORT_SYMBOL(snd_pcm_hw_constraint_mask64);
/**
* snd_pcm_hw_constraint_integer - apply an integer constraint to an interval
@@ -1199,6 +1248,9 @@ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_par
* @var: hw_params variable to apply the integer constraint
*
* Apply the constraint of integer to an interval parameter.
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var)
{
@@ -1216,6 +1268,9 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_integer);
* @max: the maximal value
*
* Apply the min/max range constraint to an interval parameter.
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
*/
int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
unsigned int min, unsigned int max)
@@ -1247,6 +1302,8 @@ static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params,
* @l: list
*
* Apply the list of constraints to an interval parameter.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1281,6 +1338,8 @@ static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
* @cond: condition bits
* @var: hw_params variable to apply the ratnums constraint
* @r: struct snd_ratnums constriants
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1314,6 +1373,8 @@ static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
* @cond: condition bits
* @var: hw_params variable to apply the ratdens constraint
* @r: struct snd_ratdens constriants
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1345,6 +1406,8 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params,
* @cond: condition bits
* @width: sample bits width
* @msbits: msbits width
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1373,6 +1436,8 @@ static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params,
* @cond: condition bits
* @var: hw_params variable to apply the step constraint
* @step: step size
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1403,6 +1468,8 @@ static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm
* @runtime: PCM runtime instance
* @cond: condition bits
* @var: hw_params variable to apply the power-of-2 constraint
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
unsigned int cond,
@@ -1429,6 +1496,8 @@ static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params,
* snd_pcm_hw_rule_noresample - add a rule to allow disabling hw resampling
* @runtime: PCM runtime instance
* @base_rate: the rate at which the hardware does not resample
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime,
unsigned int base_rate)
@@ -1478,8 +1547,8 @@ EXPORT_SYMBOL(_snd_pcm_hw_params_any);
* @var: parameter to retrieve
* @dir: pointer to the direction (-1,0,1) or %NULL
*
- * Return the value for field @var if it's fixed in configuration space
- * defined by @params. Return -%EINVAL otherwise.
+ * Return: The value for field @var if it's fixed in configuration space
+ * defined by @params. -%EINVAL otherwise.
*/
int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, int *dir)
@@ -1550,7 +1619,8 @@ static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params,
*
* Inside configuration space defined by @params remove from @var all
* values > minimum. Reduce configuration space accordingly.
- * Return the minimum.
+ *
+ * Return: The minimum, or a negative error code on failure.
*/
int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,
struct snd_pcm_hw_params *params,
@@ -1596,7 +1666,8 @@ static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params,
*
* Inside configuration space defined by @params remove from @var all
* values < maximum. Reduce configuration space accordingly.
- * Return the maximum.
+ *
+ * Return: The maximum, or a negative error code on failure.
*/
int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,
struct snd_pcm_hw_params *params,
@@ -1624,6 +1695,8 @@ EXPORT_SYMBOL(snd_pcm_hw_param_last);
* The configuration chosen is that obtained fixing in this order:
* first access, first format, first subformat, min channels,
* min rate, min period time, max buffer size, min tick time
+ *
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
struct snd_pcm_hw_params *params)
@@ -1661,8 +1734,10 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream,
if (snd_pcm_running(substream) &&
snd_pcm_update_hw_ptr(substream) >= 0)
runtime->status->hw_ptr %= runtime->buffer_size;
- else
+ else {
runtime->status->hw_ptr = 0;
+ runtime->hw_ptr_wrap = 0;
+ }
snd_pcm_stream_unlock_irqrestore(substream, flags);
return 0;
}
@@ -1728,7 +1803,7 @@ static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
* Processes the generic ioctl commands for PCM.
* Can be passed as the ioctl callback for PCM ops.
*
- * Returns zero if successful, or a negative error code on failure.
+ * Return: Zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
@@ -1861,10 +1936,13 @@ static int wait_for_avail(struct snd_pcm_substream *substream,
case SNDRV_PCM_STATE_DISCONNECTED:
err = -EBADFD;
goto _endloop;
+ case SNDRV_PCM_STATE_PAUSED:
+ continue;
}
if (!tout) {
- snd_printd("%s write error (DMA or IRQ trouble?)\n",
- is_playback ? "playback" : "capture");
+ pcm_dbg(substream->pcm,
+ "%s write error (DMA or IRQ trouble?)\n",
+ is_playback ? "playback" : "capture");
err = -EIO;
break;
}
@@ -2467,7 +2545,7 @@ static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol)
* @info_ret: store struct snd_pcm_chmap instance if non-NULL
*
* Create channel-mapping control elements assigned to the given PCM stream(s).
- * Returns zero if succeed, or a negative error value.
+ * Return: Zero if successful, or a negative error value.
*/
int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
const struct snd_pcm_chmap_elem *chmap,