[alsa-devel] [RFC PATCH] ALSA: split params refinement to seperated file
ALSA parameter refinement takes up about 13kB of memory. The refinement is useful on the desktop but is not usually required for some bespoke applications typically found on small inexpensive devices with reduced memory. These bespoke applications will typically configure the audio configuration with the exact requirements that will not require any further refinement from the kernel.
The change moves refinement functions from original pcm_native.c and pcm_lib.c, to pcm_refine.c(similarly the declaration from pcm.h to pcm_refine.h), so it is not so big as it looks like.
Please be noticed that this change may block alsa-lib usage, user app such as aplay/arecord doesn't work when CONFIG_SND_PARAMS_REFINEMENT is unselected. But it may be still worthy, some small player(e.g. tinyplay) can work with fixed params passed in(some small changes for the player may be needed), at this case, the change here will save about 13KB footprint on x86_64 platform for those small inexpensive devices(e.g. IoT ones).
Signed-off-by: Jie Yang yang.jie@intel.com --- include/sound/pcm.h | 123 +--- include/sound/pcm_refine.h | 307 +++++++++ sound/core/Kconfig | 9 + sound/core/Makefile | 1 + sound/core/pcm_lib.c | 982 ---------------------------- sound/core/pcm_native.c | 505 --------------- sound/core/pcm_refine.c | 1519 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1837 insertions(+), 1609 deletions(-) create mode 100644 include/sound/pcm_refine.h create mode 100644 sound/core/pcm_refine.c
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 691e7ee..f2d6425 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -26,6 +26,7 @@ #include <sound/asound.h> #include <sound/memalloc.h> #include <sound/minors.h> +#include <sound/pcm_refine.h> #include <linux/poll.h> #include <linux/mm.h> #include <linux/bitops.h> @@ -213,34 +214,6 @@ struct snd_pcm_ops { #define SNDRV_PCM_FMTBIT_IEC958_SUBFRAME SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE #endif
-struct snd_pcm_file { - struct snd_pcm_substream *substream; - int no_compat_mmap; -}; - -struct snd_pcm_hw_rule; -typedef int (*snd_pcm_hw_rule_func_t)(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule); - -struct snd_pcm_hw_rule { - unsigned int cond; - int var; - int deps[4]; - - snd_pcm_hw_rule_func_t func; - void *private; -}; - -struct snd_pcm_hw_constraints { - struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; - struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; - unsigned int rules_num; - unsigned int rules_all; - struct snd_pcm_hw_rule *rules; -}; - static inline struct snd_mask *constrs_mask(struct snd_pcm_hw_constraints *constrs, snd_pcm_hw_param_t var) { @@ -253,38 +226,6 @@ static inline struct snd_interval *constrs_interval(struct snd_pcm_hw_constraint return &constrs->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; }
-struct snd_ratnum { - unsigned int num; - unsigned int den_min, den_max, den_step; -}; - -struct snd_ratden { - unsigned int num_min, num_max, num_step; - unsigned int den; -}; - -struct snd_pcm_hw_constraint_ratnums { - int nrats; - struct snd_ratnum *rats; -}; - -struct snd_pcm_hw_constraint_ratdens { - int nrats; - struct snd_ratden *rats; -}; - -struct snd_pcm_hw_constraint_list { - const unsigned int *list; - unsigned int count; - unsigned int mask; -}; - -struct snd_pcm_hw_constraint_ranges { - unsigned int count; - const struct snd_interval *ranges; - unsigned int mask; -}; - struct snd_pcm_hwptr_log;
/* @@ -968,72 +909,10 @@ static inline unsigned int params_buffer_bytes(const struct snd_pcm_hw_params *p return hw_param_interval_c(p, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min; }
-int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v); -void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c); -void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c); -void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, - unsigned int k, struct snd_interval *c); -void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, - const struct snd_interval *b, struct snd_interval *c); -int snd_interval_list(struct snd_interval *i, unsigned int count, - const unsigned int *list, unsigned int mask); -int snd_interval_ranges(struct snd_interval *i, unsigned int count, - const struct snd_interval *list, unsigned int mask); -int snd_interval_ratnum(struct snd_interval *i, - unsigned int rats_count, struct snd_ratnum *rats, - unsigned int *nump, unsigned int *denp); - void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params); void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var); int snd_pcm_hw_params_choose(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params);
-int snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); - -int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream); -int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream); - -int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, - u_int32_t mask); -int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, - u_int64_t mask); -int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, - unsigned int min, unsigned int max); -int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var); -int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, - unsigned int cond, - snd_pcm_hw_param_t var, - const struct snd_pcm_hw_constraint_list *l); -int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, - unsigned int cond, - snd_pcm_hw_param_t var, - const struct snd_pcm_hw_constraint_ranges *r); -int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, - unsigned int cond, - snd_pcm_hw_param_t var, - struct snd_pcm_hw_constraint_ratnums *r); -int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, - unsigned int cond, - snd_pcm_hw_param_t var, - struct snd_pcm_hw_constraint_ratdens *r); -int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, - unsigned int cond, - unsigned int width, - unsigned int msbits); -int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, - unsigned int cond, - snd_pcm_hw_param_t var, - unsigned long step); -int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, - unsigned int cond, - snd_pcm_hw_param_t var); -int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime, - unsigned int base_rate); -int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, - unsigned int cond, - int var, - snd_pcm_hw_rule_func_t func, void *private, - int dep, ...); - int snd_pcm_format_signed(snd_pcm_format_t format); int snd_pcm_format_unsigned(snd_pcm_format_t format); int snd_pcm_format_linear(snd_pcm_format_t format); diff --git a/include/sound/pcm_refine.h b/include/sound/pcm_refine.h new file mode 100644 index 0000000..2e47620 --- /dev/null +++ b/include/sound/pcm_refine.h @@ -0,0 +1,307 @@ +#ifndef __SOUND_PCM_REFINE_H +#define __SOUND_PCM_REFINE_H + +/* + * Digital Audio (PCM) HW/SW params refinement layer + * Copyright (c) by Jaroslav Kysela perex@perex.cz + * Abramo Bagnara abramo@alsa-project.org + * Jie Yang yang.jie@intel.com + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +struct snd_pcm_file { + struct snd_pcm_substream *substream; + int no_compat_mmap; +}; + +struct snd_pcm_hw_rule; +typedef int (*snd_pcm_hw_rule_func_t)(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule); + +struct snd_pcm_hw_rule { + unsigned int cond; + int var; + int deps[4]; + + snd_pcm_hw_rule_func_t func; + void *private; +}; + +struct snd_pcm_hw_constraints { + struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - + SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; + struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + unsigned int rules_num; + unsigned int rules_all; + struct snd_pcm_hw_rule *rules; +}; + +struct snd_ratnum { + unsigned int num; + unsigned int den_min, den_max, den_step; +}; + +struct snd_ratden { + unsigned int num_min, num_max, num_step; + unsigned int den; +}; + +struct snd_pcm_hw_constraint_ratnums { + int nrats; + struct snd_ratnum *rats; +}; + +struct snd_pcm_hw_constraint_ratdens { + int nrats; + struct snd_ratden *rats; +}; + +struct snd_pcm_hw_constraint_list { + const unsigned int *list; + unsigned int count; + unsigned int mask; +}; + +struct snd_pcm_hw_constraint_ranges { + unsigned int count; + const struct snd_interval *ranges; + unsigned int mask; +}; + +struct snd_pcm_runtime; + +#ifdef CONFIG_SND_PARAMS_REFINEMENT +int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v); +void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c); +void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c); +void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, + unsigned int k, struct snd_interval *c); +void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, + const struct snd_interval *b, struct snd_interval *c); +int snd_interval_list(struct snd_interval *i, unsigned int count, + const unsigned int *list, unsigned int mask); +int snd_interval_ranges(struct snd_interval *i, unsigned int count, + const struct snd_interval *list, unsigned int mask); +int snd_interval_ratnum(struct snd_interval *i, + unsigned int rats_count, struct snd_ratnum *rats, + unsigned int *nump, unsigned int *denp); + + +int snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); + +int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream); +int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream); + +int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, + u_int32_t mask); +int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, + u_int64_t mask); +int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, + unsigned int min, unsigned int max); +int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var); +int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + const struct snd_pcm_hw_constraint_list *l); +int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + const struct snd_pcm_hw_constraint_ranges *r); +int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + struct snd_pcm_hw_constraint_ratnums *r); +int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + struct snd_pcm_hw_constraint_ratdens *r); +int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, + unsigned int cond, + unsigned int width, + unsigned int msbits); +int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + unsigned long step); +int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var); +int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime, + unsigned int base_rate); +int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, + unsigned int cond, + int var, + snd_pcm_hw_rule_func_t func, void *private, + int dep, ...); + +#else +static inline int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v) +{ + return 0; +} + +static inline void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) +{ +} + +static inline void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) +{ +} + +static inline void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, + unsigned int k, struct snd_interval *c) +{ +} + +static inline void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, + const struct snd_interval *b, struct snd_interval *c) +{ +} + +static inline int snd_interval_list(struct snd_interval *i, unsigned int count, + const unsigned int *list, unsigned int mask) +{ + return 0; +} + +static inline int snd_interval_ranges(struct snd_interval *i, unsigned int count, + const struct snd_interval *list, unsigned int mask) +{ + return 0; +} + +static inline int snd_interval_ratnum(struct snd_interval *i, + unsigned int rats_count, struct snd_ratnum *rats, + unsigned int *nump, unsigned int *denp) +{ + return 0; +} + +static inline int snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) +{ + return 0; +} + +static inline int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream) +{ + return 0; +} + +static inline int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) +{ + return 0; +} + +static inline int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, + u_int32_t mask) +{ + return 0; +} + +static inline int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, + u_int64_t mask) +{ + return 0; +} + +static inline int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, + unsigned int min, unsigned int max) +{ + return 0; +} + +static inline int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var) +{ + return 0; +} + +static inline int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + const struct snd_pcm_hw_constraint_list *l) +{ + return 0; +} + +static inline int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + const struct snd_pcm_hw_constraint_ranges *r) +{ + return 0; +} + +static inline int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + struct snd_pcm_hw_constraint_ratnums *r) +{ + return 0; +} + +static inline int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + struct snd_pcm_hw_constraint_ratdens *r) +{ + return 0; +} + +static inline int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, + unsigned int cond, + unsigned int width, + unsigned int msbits) +{ + return 0; +} + +static inline int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + unsigned long step) +{ + return 0; +} + +static inline int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var) +{ + return 0; +} + +static inline int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime, + unsigned int base_rate) +{ + return 0; +} + +static inline int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, + unsigned int cond, + int var, + snd_pcm_hw_rule_func_t func, void *private, + int dep, ...) +{ + return 0; +} + +#endif + +#endif /* __SOUND_PCM_REFINE_H */ + diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 6c96fee..644e837 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -191,6 +191,15 @@ config SND_PROC_FS 9KB on x86_64 platform. If unsure say Y.
+config SND_PARAMS_REFINEMENT + bool "HW/SW Params Refinement Support" if EXPERT + default y + help + Say 'N' to disable the HW/SW Params Refinement feature, which may + save about 13KB code size on x86_64 object target. + Please be noted that enabling this may break alsa on the desktop, + only use it with tinyalsa/tinyplay for device such as IoT. + config SND_VERBOSE_PROCFS bool "Verbose procfs contents" depends on SND_PROC_FS diff --git a/sound/core/Makefile b/sound/core/Makefile index 3354f91..c615404 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -18,6 +18,7 @@ snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o +snd-pcm-$(CONFIG_SND_PARAMS_REFINEMENT) += pcm_refine.o
# for trace-points CFLAGS_pcm_lib.o := -I$(src) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 7d45645..0bcc3ec 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -532,119 +532,6 @@ EXPORT_SYMBOL(snd_pcm_set_sync); * Standard ioctl routine */
-static inline unsigned int div32(unsigned int a, unsigned int b, - unsigned int *r) -{ - if (b == 0) { - *r = 0; - return UINT_MAX; - } - *r = a % b; - return a / b; -} - -static inline unsigned int div_down(unsigned int a, unsigned int b) -{ - if (b == 0) - return UINT_MAX; - return a / b; -} - -static inline unsigned int div_up(unsigned int a, unsigned int b) -{ - unsigned int r; - unsigned int q; - if (b == 0) - return UINT_MAX; - q = div32(a, b, &r); - if (r) - ++q; - return q; -} - -static inline unsigned int mul(unsigned int a, unsigned int b) -{ - if (a == 0) - return 0; - if (div_down(UINT_MAX, a) < b) - return UINT_MAX; - return a * b; -} - -static inline unsigned int muldiv32(unsigned int a, unsigned int b, - unsigned int c, unsigned int *r) -{ - u_int64_t n = (u_int64_t) a * b; - if (c == 0) { - snd_BUG_ON(!n); - *r = 0; - return UINT_MAX; - } - n = div_u64_rem(n, c, r); - if (n >= UINT_MAX) { - *r = 0; - return UINT_MAX; - } - return n; -} - -/** - * snd_interval_refine - refine the interval value of configurator - * @i: the interval value to refine - * @v: the interval value to refer to - * - * Refines the interval value with the reference value. - * The interval is changed to the range satisfying both intervals. - * The interval status (min, max, integer, etc.) are evaluated. - * - * 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) -{ - int changed = 0; - if (snd_BUG_ON(snd_interval_empty(i))) - return -EINVAL; - if (i->min < v->min) { - i->min = v->min; - i->openmin = v->openmin; - changed = 1; - } else if (i->min == v->min && !i->openmin && v->openmin) { - i->openmin = 1; - changed = 1; - } - if (i->max > v->max) { - i->max = v->max; - i->openmax = v->openmax; - changed = 1; - } else if (i->max == v->max && !i->openmax && v->openmax) { - i->openmax = 1; - changed = 1; - } - if (!i->integer && v->integer) { - i->integer = 1; - changed = 1; - } - if (i->integer) { - if (i->openmin) { - i->min++; - i->openmin = 0; - } - if (i->openmax) { - i->max--; - i->openmax = 0; - } - } else if (!i->openmin && !i->openmax && i->min == i->max) - i->integer = 1; - if (snd_interval_checkempty(i)) { - snd_interval_none(i); - return -EINVAL; - } - return changed; -} - -EXPORT_SYMBOL(snd_interval_refine); - static int snd_interval_refine_first(struct snd_interval *i) { if (snd_BUG_ON(snd_interval_empty(i))) @@ -671,875 +558,6 @@ static int snd_interval_refine_last(struct snd_interval *i) return 1; }
-void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) -{ - if (a->empty || b->empty) { - snd_interval_none(c); - return; - } - c->empty = 0; - c->min = mul(a->min, b->min); - c->openmin = (a->openmin || b->openmin); - c->max = mul(a->max, b->max); - c->openmax = (a->openmax || b->openmax); - c->integer = (a->integer && b->integer); -} - -/** - * snd_interval_div - refine the interval value with division - * @a: dividend - * @b: divisor - * @c: quotient - * - * c = a / b - * - * Returns non-zero if the value is changed, zero if not changed. - */ -void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) -{ - unsigned int r; - if (a->empty || b->empty) { - snd_interval_none(c); - return; - } - c->empty = 0; - c->min = div32(a->min, b->max, &r); - c->openmin = (r || a->openmin || b->openmax); - if (b->min > 0) { - c->max = div32(a->max, b->min, &r); - if (r) { - c->max++; - c->openmax = 1; - } else - c->openmax = (a->openmax || b->openmin); - } else { - c->max = UINT_MAX; - c->openmax = 0; - } - c->integer = 0; -} - -/** - * snd_interval_muldivk - refine the interval value - * @a: dividend 1 - * @b: dividend 2 - * @k: divisor (as integer) - * @c: result - * - * c = a * b / k - * - * Returns non-zero if the value is changed, zero if not changed. - */ -void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, - unsigned int k, struct snd_interval *c) -{ - unsigned int r; - if (a->empty || b->empty) { - snd_interval_none(c); - return; - } - c->empty = 0; - c->min = muldiv32(a->min, b->min, k, &r); - c->openmin = (r || a->openmin || b->openmin); - c->max = muldiv32(a->max, b->max, k, &r); - if (r) { - c->max++; - c->openmax = 1; - } else - c->openmax = (a->openmax || b->openmax); - c->integer = 0; -} - -/** - * snd_interval_mulkdiv - refine the interval value - * @a: dividend 1 - * @k: dividend 2 (as integer) - * @b: divisor - * @c: result - * - * c = a * k / b - * - * Returns non-zero if the value is changed, zero if not changed. - */ -void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, - const struct snd_interval *b, struct snd_interval *c) -{ - unsigned int r; - if (a->empty || b->empty) { - snd_interval_none(c); - return; - } - c->empty = 0; - c->min = muldiv32(a->min, k, b->max, &r); - c->openmin = (r || a->openmin || b->openmax); - if (b->min > 0) { - c->max = muldiv32(a->max, k, b->min, &r); - if (r) { - c->max++; - c->openmax = 1; - } else - c->openmax = (a->openmax || b->openmin); - } else { - c->max = UINT_MAX; - c->openmax = 0; - } - c->integer = 0; -} - -/* ---- */ - - -/** - * snd_interval_ratnum - refine the interval value - * @i: interval to refine - * @rats_count: number of ratnum_t - * @rats: ratnum_t array - * @nump: pointer to store the resultant numerator - * @denp: pointer to store the resultant denominator - * - * 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, - unsigned int *nump, unsigned int *denp) -{ - unsigned int best_num, best_den; - int best_diff; - unsigned int k; - struct snd_interval t; - int err; - unsigned int result_num, result_den; - int result_diff; - - best_num = best_den = best_diff = 0; - for (k = 0; k < rats_count; ++k) { - unsigned int num = rats[k].num; - unsigned int den; - unsigned int q = i->min; - int diff; - if (q == 0) - q = 1; - den = div_up(num, q); - if (den < rats[k].den_min) - continue; - if (den > rats[k].den_max) - den = rats[k].den_max; - else { - unsigned int r; - r = (den - rats[k].den_min) % rats[k].den_step; - if (r != 0) - den -= r; - } - diff = num - q * den; - if (diff < 0) - diff = -diff; - if (best_num == 0 || - diff * best_den < best_diff * den) { - best_diff = diff; - best_den = den; - best_num = num; - } - } - if (best_den == 0) { - i->empty = 1; - return -EINVAL; - } - t.min = div_down(best_num, best_den); - t.openmin = !!(best_num % best_den); - - result_num = best_num; - result_diff = best_diff; - result_den = best_den; - best_num = best_den = best_diff = 0; - for (k = 0; k < rats_count; ++k) { - unsigned int num = rats[k].num; - unsigned int den; - unsigned int q = i->max; - int diff; - if (q == 0) { - i->empty = 1; - return -EINVAL; - } - den = div_down(num, q); - if (den > rats[k].den_max) - continue; - if (den < rats[k].den_min) - den = rats[k].den_min; - else { - unsigned int r; - r = (den - rats[k].den_min) % rats[k].den_step; - if (r != 0) - den += rats[k].den_step - r; - } - diff = q * den - num; - if (diff < 0) - diff = -diff; - if (best_num == 0 || - diff * best_den < best_diff * den) { - best_diff = diff; - best_den = den; - best_num = num; - } - } - if (best_den == 0) { - i->empty = 1; - return -EINVAL; - } - t.max = div_up(best_num, best_den); - t.openmax = !!(best_num % best_den); - t.integer = 0; - err = snd_interval_refine(i, &t); - if (err < 0) - return err; - - if (snd_interval_single(i)) { - if (best_diff * result_den < result_diff * best_den) { - result_num = best_num; - result_den = best_den; - } - if (nump) - *nump = result_num; - if (denp) - *denp = result_den; - } - return err; -} - -EXPORT_SYMBOL(snd_interval_ratnum); - -/** - * snd_interval_ratden - refine the interval value - * @i: interval to refine - * @rats_count: number of struct ratden - * @rats: struct ratden array - * @nump: pointer to store the resultant numerator - * @denp: pointer to store the resultant denominator - * - * 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, - unsigned int *nump, unsigned int *denp) -{ - unsigned int best_num, best_diff, best_den; - unsigned int k; - struct snd_interval t; - int err; - - best_num = best_den = best_diff = 0; - for (k = 0; k < rats_count; ++k) { - unsigned int num; - unsigned int den = rats[k].den; - unsigned int q = i->min; - int diff; - num = mul(q, den); - if (num > rats[k].num_max) - continue; - if (num < rats[k].num_min) - num = rats[k].num_max; - else { - unsigned int r; - r = (num - rats[k].num_min) % rats[k].num_step; - if (r != 0) - num += rats[k].num_step - r; - } - diff = num - q * den; - if (best_num == 0 || - diff * best_den < best_diff * den) { - best_diff = diff; - best_den = den; - best_num = num; - } - } - if (best_den == 0) { - i->empty = 1; - return -EINVAL; - } - t.min = div_down(best_num, best_den); - t.openmin = !!(best_num % best_den); - - best_num = best_den = best_diff = 0; - for (k = 0; k < rats_count; ++k) { - unsigned int num; - unsigned int den = rats[k].den; - unsigned int q = i->max; - int diff; - num = mul(q, den); - if (num < rats[k].num_min) - continue; - if (num > rats[k].num_max) - num = rats[k].num_max; - else { - unsigned int r; - r = (num - rats[k].num_min) % rats[k].num_step; - if (r != 0) - num -= r; - } - diff = q * den - num; - if (best_num == 0 || - diff * best_den < best_diff * den) { - best_diff = diff; - best_den = den; - best_num = num; - } - } - if (best_den == 0) { - i->empty = 1; - return -EINVAL; - } - t.max = div_up(best_num, best_den); - t.openmax = !!(best_num % best_den); - t.integer = 0; - err = snd_interval_refine(i, &t); - if (err < 0) - return err; - - if (snd_interval_single(i)) { - if (nump) - *nump = best_num; - if (denp) - *denp = best_den; - } - return err; -} - -/** - * snd_interval_list - refine the interval value from the list - * @i: the interval value to refine - * @count: the number of elements in the list - * @list: the value list - * @mask: the bit-mask to evaluate - * - * Refines the interval value from the list. - * When mask is non-zero, only the elements corresponding to bit 1 are - * evaluated. - * - * 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) -{ - unsigned int k; - struct snd_interval list_range; - - if (!count) { - i->empty = 1; - return -EINVAL; - } - snd_interval_any(&list_range); - list_range.min = UINT_MAX; - list_range.max = 0; - for (k = 0; k < count; k++) { - if (mask && !(mask & (1 << k))) - continue; - if (!snd_interval_test(i, list[k])) - continue; - list_range.min = min(list_range.min, list[k]); - list_range.max = max(list_range.max, list[k]); - } - return snd_interval_refine(i, &list_range); -} - -EXPORT_SYMBOL(snd_interval_list); - -/** - * snd_interval_ranges - refine the interval value from the list of ranges - * @i: the interval value to refine - * @count: the number of elements in the list of ranges - * @ranges: the ranges list - * @mask: the bit-mask to evaluate - * - * Refines the interval value from the list of ranges. - * When mask is non-zero, only the elements corresponding to bit 1 are - * evaluated. - * - * Return: Positive if the value is changed, zero if it's not changed, or a - * negative error code. - */ -int snd_interval_ranges(struct snd_interval *i, unsigned int count, - const struct snd_interval *ranges, unsigned int mask) -{ - unsigned int k; - struct snd_interval range_union; - struct snd_interval range; - - if (!count) { - snd_interval_none(i); - return -EINVAL; - } - snd_interval_any(&range_union); - range_union.min = UINT_MAX; - range_union.max = 0; - for (k = 0; k < count; k++) { - if (mask && !(mask & (1 << k))) - continue; - snd_interval_copy(&range, &ranges[k]); - if (snd_interval_refine(&range, i) < 0) - continue; - if (snd_interval_empty(&range)) - continue; - - if (range.min < range_union.min) { - range_union.min = range.min; - range_union.openmin = 1; - } - if (range.min == range_union.min && !range.openmin) - range_union.openmin = 0; - if (range.max > range_union.max) { - range_union.max = range.max; - range_union.openmax = 1; - } - if (range.max == range_union.max && !range.openmax) - range_union.openmax = 0; - } - return snd_interval_refine(i, &range_union); -} -EXPORT_SYMBOL(snd_interval_ranges); - -static int snd_interval_step(struct snd_interval *i, unsigned int step) -{ - unsigned int n; - int changed = 0; - n = i->min % step; - if (n != 0 || i->openmin) { - i->min += step - n; - i->openmin = 0; - changed = 1; - } - n = i->max % step; - if (n != 0 || i->openmax) { - i->max -= n; - i->openmax = 0; - changed = 1; - } - if (snd_interval_checkempty(i)) { - i->empty = 1; - return -EINVAL; - } - return changed; -} - -/* Info constraints helpers */ - -/** - * snd_pcm_hw_rule_add - add the hw-constraint rule - * @runtime: the pcm runtime instance - * @cond: condition bits - * @var: the variable to evaluate - * @func: the evaluation function - * @private: the private data pointer passed to function - * @dep: the dependent variables - * - * 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, - snd_pcm_hw_rule_func_t func, void *private, - int dep, ...) -{ - struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; - struct snd_pcm_hw_rule *c; - unsigned int k; - va_list args; - va_start(args, dep); - if (constrs->rules_num >= constrs->rules_all) { - struct snd_pcm_hw_rule *new; - unsigned int new_rules = constrs->rules_all + 16; - new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL); - if (!new) { - va_end(args); - return -ENOMEM; - } - if (constrs->rules) { - memcpy(new, constrs->rules, - constrs->rules_num * sizeof(*c)); - kfree(constrs->rules); - } - constrs->rules = new; - constrs->rules_all = new_rules; - } - c = &constrs->rules[constrs->rules_num]; - c->cond = cond; - c->func = func; - c->var = var; - c->private = private; - k = 0; - while (1) { - if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) { - va_end(args); - return -EINVAL; - } - c->deps[k++] = dep; - if (dep < 0) - break; - dep = va_arg(args, int); - } - constrs->rules_num++; - va_end(args); - return 0; -} - -EXPORT_SYMBOL(snd_pcm_hw_rule_add); - -/** - * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint - * @runtime: PCM runtime instance - * @var: hw_params variable to apply the mask - * @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) -{ - struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; - struct snd_mask *maskp = constrs_mask(constrs, var); - *maskp->bits &= mask; - memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */ - if (*maskp->bits == 0) - return -EINVAL; - return 0; -} - -/** - * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint - * @runtime: PCM runtime instance - * @var: hw_params variable to apply the mask - * @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) -{ - struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; - struct snd_mask *maskp = constrs_mask(constrs, var); - maskp->bits[0] &= (u_int32_t)mask; - maskp->bits[1] &= (u_int32_t)(mask >> 32); - memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ - if (! maskp->bits[0] && ! maskp->bits[1]) - return -EINVAL; - return 0; -} -EXPORT_SYMBOL(snd_pcm_hw_constraint_mask64); - -/** - * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval - * @runtime: PCM runtime instance - * @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) -{ - struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; - return snd_interval_setinteger(constrs_interval(constrs, var)); -} - -EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); - -/** - * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval - * @runtime: PCM runtime instance - * @var: hw_params variable to apply the range - * @min: the minimal value - * @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) -{ - struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; - struct snd_interval t; - t.min = min; - t.max = max; - t.openmin = t.openmax = 0; - t.integer = 0; - return snd_interval_refine(constrs_interval(constrs, var), &t); -} - -EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); - -static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_pcm_hw_constraint_list *list = rule->private; - return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask); -} - - -/** - * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter - * @runtime: PCM runtime instance - * @cond: condition bits - * @var: hw_params variable to apply the list constraint - * @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, - snd_pcm_hw_param_t var, - const struct snd_pcm_hw_constraint_list *l) -{ - return snd_pcm_hw_rule_add(runtime, cond, var, - snd_pcm_hw_rule_list, (void *)l, - var, -1); -} - -EXPORT_SYMBOL(snd_pcm_hw_constraint_list); - -static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_pcm_hw_constraint_ranges *r = rule->private; - return snd_interval_ranges(hw_param_interval(params, rule->var), - r->count, r->ranges, r->mask); -} - - -/** - * snd_pcm_hw_constraint_ranges - apply list of range constraints to a parameter - * @runtime: PCM runtime instance - * @cond: condition bits - * @var: hw_params variable to apply the list of range constraints - * @r: ranges - * - * Apply the list of range constraints to an interval parameter. - * - * Return: Zero if successful, or a negative error code on failure. - */ -int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, - unsigned int cond, - snd_pcm_hw_param_t var, - const struct snd_pcm_hw_constraint_ranges *r) -{ - return snd_pcm_hw_rule_add(runtime, cond, var, - snd_pcm_hw_rule_ranges, (void *)r, - var, -1); -} -EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges); - -static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_pcm_hw_constraint_ratnums *r = rule->private; - unsigned int num = 0, den = 0; - int err; - err = snd_interval_ratnum(hw_param_interval(params, rule->var), - r->nrats, r->rats, &num, &den); - if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { - params->rate_num = num; - params->rate_den = den; - } - return err; -} - -/** - * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter - * @runtime: PCM runtime instance - * @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, - snd_pcm_hw_param_t var, - struct snd_pcm_hw_constraint_ratnums *r) -{ - return snd_pcm_hw_rule_add(runtime, cond, var, - snd_pcm_hw_rule_ratnums, r, - var, -1); -} - -EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); - -static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_pcm_hw_constraint_ratdens *r = rule->private; - unsigned int num = 0, den = 0; - int err = snd_interval_ratden(hw_param_interval(params, rule->var), - r->nrats, r->rats, &num, &den); - if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { - params->rate_num = num; - params->rate_den = den; - } - return err; -} - -/** - * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter - * @runtime: PCM runtime instance - * @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, - snd_pcm_hw_param_t var, - struct snd_pcm_hw_constraint_ratdens *r) -{ - return snd_pcm_hw_rule_add(runtime, cond, var, - snd_pcm_hw_rule_ratdens, r, - var, -1); -} - -EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); - -static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - unsigned int l = (unsigned long) rule->private; - int width = l & 0xffff; - unsigned int msbits = l >> 16; - struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); - - if (!snd_interval_single(i)) - return 0; - - if ((snd_interval_value(i) == width) || - (width == 0 && snd_interval_value(i) > msbits)) - params->msbits = min_not_zero(params->msbits, msbits); - - return 0; -} - -/** - * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule - * @runtime: PCM runtime instance - * @cond: condition bits - * @width: sample bits width - * @msbits: msbits width - * - * This constraint will set the number of most significant bits (msbits) if a - * sample format with the specified width has been select. If width is set to 0 - * the msbits will be set for any sample format with a width larger than the - * specified msbits. - * - * 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, - unsigned int width, - unsigned int msbits) -{ - unsigned long l = (msbits << 16) | width; - return snd_pcm_hw_rule_add(runtime, cond, -1, - snd_pcm_hw_rule_msbits, - (void*) l, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); -} - -EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); - -static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - unsigned long step = (unsigned long) rule->private; - return snd_interval_step(hw_param_interval(params, rule->var), step); -} - -/** - * snd_pcm_hw_constraint_step - add a hw constraint step rule - * @runtime: PCM runtime instance - * @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, - snd_pcm_hw_param_t var, - unsigned long step) -{ - return snd_pcm_hw_rule_add(runtime, cond, var, - snd_pcm_hw_rule_step, (void *) step, - var, -1); -} - -EXPORT_SYMBOL(snd_pcm_hw_constraint_step); - -static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) -{ - static unsigned int pow2_sizes[] = { - 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, - 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, - 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, - 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30 - }; - return snd_interval_list(hw_param_interval(params, rule->var), - ARRAY_SIZE(pow2_sizes), pow2_sizes, 0); -} - -/** - * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule - * @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, - snd_pcm_hw_param_t var) -{ - return snd_pcm_hw_rule_add(runtime, cond, var, - snd_pcm_hw_rule_pow2, NULL, - var, -1); -} - -EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); - -static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - unsigned int base_rate = (unsigned int)(uintptr_t)rule->private; - struct snd_interval *rate; - - rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - return snd_interval_list(rate, 1, &base_rate, 0); -} - -/** - * 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) -{ - return snd_pcm_hw_rule_add(runtime, SNDRV_PCM_HW_PARAMS_NORESAMPLE, - SNDRV_PCM_HW_PARAM_RATE, - snd_pcm_hw_rule_noresample_func, - (void *)(uintptr_t)base_rate, - SNDRV_PCM_HW_PARAM_RATE, -1); -} -EXPORT_SYMBOL(snd_pcm_hw_rule_noresample); - static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index d126c03..b4c4829 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -227,21 +227,6 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, return err; }
-static bool hw_support_mmap(struct snd_pcm_substream *substream) -{ - if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP)) - return false; - /* check architectures that return -EINVAL from dma_mmap_coherent() */ - /* FIXME: this should be some global flag */ -#if defined(CONFIG_C6X) || defined(CONFIG_FRV) || defined(CONFIG_MN10300) ||\ - defined(CONFIG_PARISC) || defined(CONFIG_XTENSA) - if (!substream->ops->mmap && - substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) - return false; -#endif - return true; -} - #undef RULES_DEBUG
#ifdef RULES_DEBUG @@ -265,184 +250,6 @@ static const char * const snd_pcm_hw_param_names[] = { }; #endif
-int snd_pcm_hw_refine(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - unsigned int k; - struct snd_pcm_hardware *hw; - struct snd_interval *i = NULL; - struct snd_mask *m = NULL; - struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; - unsigned int rstamps[constrs->rules_num]; - unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; - unsigned int stamp = 2; - int changed, again; - - params->info = 0; - params->fifo_size = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) - params->msbits = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { - params->rate_num = 0; - params->rate_den = 0; - } - - for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { - m = hw_param_mask(params, k); - if (snd_mask_empty(m)) - return -EINVAL; - if (!(params->rmask & (1 << k))) - continue; -#ifdef RULES_DEBUG - pr_debug("%s = ", snd_pcm_hw_param_names[k]); - pr_cont("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); -#endif - changed = snd_mask_refine(m, constrs_mask(constrs, k)); -#ifdef RULES_DEBUG - pr_cont("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); -#endif - if (changed) - params->cmask |= 1 << k; - if (changed < 0) - return changed; - } - - for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { - i = hw_param_interval(params, k); - if (snd_interval_empty(i)) - return -EINVAL; - if (!(params->rmask & (1 << k))) - continue; -#ifdef RULES_DEBUG - pr_debug("%s = ", snd_pcm_hw_param_names[k]); - if (i->empty) - pr_cont("empty"); - else - pr_cont("%c%u %u%c", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); - pr_cont(" -> "); -#endif - changed = snd_interval_refine(i, constrs_interval(constrs, k)); -#ifdef RULES_DEBUG - if (i->empty) - pr_cont("empty\n"); - else - pr_cont("%c%u %u%c\n", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); -#endif - if (changed) - params->cmask |= 1 << k; - if (changed < 0) - return changed; - } - - for (k = 0; k < constrs->rules_num; k++) - rstamps[k] = 0; - for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) - vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; - do { - again = 0; - for (k = 0; k < constrs->rules_num; k++) { - struct snd_pcm_hw_rule *r = &constrs->rules[k]; - unsigned int d; - int doit = 0; - if (r->cond && !(r->cond & params->flags)) - continue; - for (d = 0; r->deps[d] >= 0; d++) { - if (vstamps[r->deps[d]] > rstamps[k]) { - doit = 1; - break; - } - } - if (!doit) - continue; -#ifdef RULES_DEBUG - pr_debug("Rule %d [%p]: ", k, r->func); - if (r->var >= 0) { - pr_cont("%s = ", snd_pcm_hw_param_names[r->var]); - if (hw_is_mask(r->var)) { - m = hw_param_mask(params, r->var); - pr_cont("%x", *m->bits); - } else { - i = hw_param_interval(params, r->var); - if (i->empty) - pr_cont("empty"); - else - pr_cont("%c%u %u%c", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); - } - } -#endif - changed = r->func(params, r); -#ifdef RULES_DEBUG - if (r->var >= 0) { - pr_cont(" -> "); - if (hw_is_mask(r->var)) - pr_cont("%x", *m->bits); - else { - if (i->empty) - pr_cont("empty"); - else - pr_cont("%c%u %u%c", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); - } - } - pr_cont("\n"); -#endif - rstamps[k] = stamp; - if (changed && r->var >= 0) { - params->cmask |= (1 << r->var); - vstamps[r->var] = stamp; - again = 1; - } - if (changed < 0) - return changed; - stamp++; - } - } while (again); - if (!params->msbits) { - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); - if (snd_interval_single(i)) - params->msbits = snd_interval_value(i); - } - - if (!params->rate_den) { - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - if (snd_interval_single(i)) { - params->rate_num = snd_interval_value(i); - params->rate_den = 1; - } - } - - hw = &substream->runtime->hw; - if (!params->info) { - params->info = hw->info & ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES | - SNDRV_PCM_INFO_DRAIN_TRIGGER); - if (!hw_support_mmap(substream)) - params->info &= ~(SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID); - } - if (!params->fifo_size) { - m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - if (snd_mask_min(m) == snd_mask_max(m) && - snd_interval_min(i) == snd_interval_max(i)) { - changed = substream->ops->ioctl(substream, - SNDRV_PCM_IOCTL1_FIFO_SIZE, params); - if (changed < 0) - return changed; - } - } - params->rmask = 0; - return 0; -} - -EXPORT_SYMBOL(snd_pcm_hw_refine); - static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params __user * _params) { @@ -1888,89 +1695,6 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream) /* * hw configurator */ -static int snd_pcm_hw_rule_mul(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_interval t; - snd_interval_mul(hw_param_interval_c(params, rule->deps[0]), - hw_param_interval_c(params, rule->deps[1]), &t); - return snd_interval_refine(hw_param_interval(params, rule->var), &t); -} - -static int snd_pcm_hw_rule_div(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_interval t; - snd_interval_div(hw_param_interval_c(params, rule->deps[0]), - hw_param_interval_c(params, rule->deps[1]), &t); - return snd_interval_refine(hw_param_interval(params, rule->var), &t); -} - -static int snd_pcm_hw_rule_muldivk(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_interval t; - snd_interval_muldivk(hw_param_interval_c(params, rule->deps[0]), - hw_param_interval_c(params, rule->deps[1]), - (unsigned long) rule->private, &t); - return snd_interval_refine(hw_param_interval(params, rule->var), &t); -} - -static int snd_pcm_hw_rule_mulkdiv(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_interval t; - snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]), - (unsigned long) rule->private, - hw_param_interval_c(params, rule->deps[1]), &t); - return snd_interval_refine(hw_param_interval(params, rule->var), &t); -} - -static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - unsigned int k; - struct snd_interval *i = hw_param_interval(params, rule->deps[0]); - struct snd_mask m; - struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - snd_mask_any(&m); - for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { - int bits; - if (! snd_mask_test(mask, k)) - continue; - bits = snd_pcm_format_physical_width(k); - if (bits <= 0) - continue; /* ignore invalid formats */ - if ((unsigned)bits < i->min || (unsigned)bits > i->max) - snd_mask_reset(&m, k); - } - return snd_mask_refine(mask, &m); -} - -static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_interval t; - unsigned int k; - t.min = UINT_MAX; - t.max = 0; - t.openmin = 0; - t.openmax = 0; - for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { - int bits; - if (! snd_mask_test(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k)) - continue; - bits = snd_pcm_format_physical_width(k); - if (bits <= 0) - continue; /* ignore invalid formats */ - if (t.min > (unsigned)bits) - t.min = bits; - if (t.max < (unsigned)bits) - t.max = bits; - } - t.integer = 1; - return snd_interval_refine(hw_param_interval(params, rule->var), &t); -}
#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 #error "Change this table" @@ -1984,235 +1708,6 @@ const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { .list = rates, };
-static int snd_pcm_hw_rule_rate(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_pcm_hardware *hw = rule->private; - return snd_interval_list(hw_param_interval(params, rule->var), - snd_pcm_known_rates.count, - snd_pcm_known_rates.list, hw->rates); -} - -static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_interval t; - struct snd_pcm_substream *substream = rule->private; - t.min = 0; - t.max = substream->buffer_bytes_max; - t.openmin = 0; - t.openmax = 0; - t.integer = 1; - return snd_interval_refine(hw_param_interval(params, rule->var), &t); -} - -int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; - int k, err; - - for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { - snd_mask_any(constrs_mask(constrs, k)); - } - - for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { - snd_interval_any(constrs_interval(constrs, k)); - } - - snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_CHANNELS)); - snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_SIZE)); - snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)); - snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)); - snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS)); - - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, - snd_pcm_hw_rule_format, NULL, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - snd_pcm_hw_rule_sample_bits, NULL, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - snd_pcm_hw_rule_div, NULL, - SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, - snd_pcm_hw_rule_mul, NULL, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, - snd_pcm_hw_rule_mulkdiv, (void*) 8, - SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, - snd_pcm_hw_rule_mulkdiv, (void*) 8, - SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_pcm_hw_rule_div, NULL, - SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - snd_pcm_hw_rule_mulkdiv, (void*) 1000000, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - snd_pcm_hw_rule_mulkdiv, (void*) 1000000, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_TIME, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, - snd_pcm_hw_rule_div, NULL, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - snd_pcm_hw_rule_div, NULL, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - snd_pcm_hw_rule_mulkdiv, (void*) 8, - SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - snd_pcm_hw_rule_muldivk, (void*) 1000000, - SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - snd_pcm_hw_rule_mul, NULL, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - snd_pcm_hw_rule_mulkdiv, (void*) 8, - SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - snd_pcm_hw_rule_muldivk, (void*) 1000000, - SNDRV_PCM_HW_PARAM_BUFFER_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - snd_pcm_hw_rule_muldivk, (void*) 8, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - snd_pcm_hw_rule_muldivk, (void*) 8, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME, - snd_pcm_hw_rule_mulkdiv, (void*) 1000000, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_TIME, - snd_pcm_hw_rule_mulkdiv, (void*) 1000000, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - return err; - return 0; -} - -int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_pcm_hardware *hw = &runtime->hw; - int err; - unsigned int mask = 0; - - if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) - mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; - if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) - mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; - if (hw_support_mmap(substream)) { - if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) - mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; - if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) - mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED; - if (hw->info & SNDRV_PCM_INFO_COMPLEX) - mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX; - } - err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask); - if (err < 0) - return err; - - err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats); - if (err < 0) - return err; - - err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD); - if (err < 0) - return err; - - err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, - hw->channels_min, hw->channels_max); - if (err < 0) - return err; - - err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, - hw->rate_min, hw->rate_max); - if (err < 0) - return err; - - err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - hw->period_bytes_min, hw->period_bytes_max); - if (err < 0) - return err; - - err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS, - hw->periods_min, hw->periods_max); - if (err < 0) - return err; - - err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - hw->period_bytes_min, hw->buffer_bytes_max); - if (err < 0) - return err; - - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - snd_pcm_hw_rule_buffer_bytes_max, substream, - SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1); - if (err < 0) - return err; - - /* FIXME: remove */ - if (runtime->dma_bytes) { - err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes); - if (err < 0) - return err; - } - - if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) { - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - snd_pcm_hw_rule_rate, hw, - SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - return err; - } - - /* FIXME: this belong to lowlevel */ - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); - - return 0; -} - static void pcm_release_private(struct snd_pcm_substream *substream) { snd_pcm_unlink(substream); diff --git a/sound/core/pcm_refine.c b/sound/core/pcm_refine.c new file mode 100644 index 0000000..40ae80e --- /dev/null +++ b/sound/core/pcm_refine.c @@ -0,0 +1,1519 @@ +/* + * Digital Audio (PCM) abstract layer + * Copyright (c) by Jaroslav Kysela perex@perex.cz + * Abramo Bagnara abramo@alsa-project.org + * Jie Yang yang.jie@intel.com + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + + + +static bool hw_support_mmap(struct snd_pcm_substream *substream) +{ + if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP)) + return false; + /* check architectures that return -EINVAL from dma_mmap_coherent() */ + /* FIXME: this should be some global flag */ +#if defined(CONFIG_C6X) || defined(CONFIG_FRV) || defined(CONFIG_MN10300) ||\ + defined(CONFIG_PARISC) || defined(CONFIG_XTENSA) + if (!substream->ops->mmap && + substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) + return false; +#endif + return true; +} + +int snd_pcm_hw_refine(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + unsigned int k; + struct snd_pcm_hardware *hw; + struct snd_interval *i = NULL; + struct snd_mask *m = NULL; + struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; + unsigned int rstamps[constrs->rules_num]; + unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; + unsigned int stamp = 2; + int changed, again; + + params->info = 0; + params->fifo_size = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) + params->msbits = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { + params->rate_num = 0; + params->rate_den = 0; + } + + for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { + m = hw_param_mask(params, k); + if (snd_mask_empty(m)) + return -EINVAL; + if (!(params->rmask & (1 << k))) + continue; +#ifdef RULES_DEBUG + pr_debug("%s = ", snd_pcm_hw_param_names[k]); + pr_cont("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); +#endif + changed = snd_mask_refine(m, constrs_mask(constrs, k)); +#ifdef RULES_DEBUG + pr_cont("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); +#endif + if (changed) + params->cmask |= 1 << k; + if (changed < 0) + return changed; + } + + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { + i = hw_param_interval(params, k); + if (snd_interval_empty(i)) + return -EINVAL; + if (!(params->rmask & (1 << k))) + continue; +#ifdef RULES_DEBUG + pr_debug("%s = ", snd_pcm_hw_param_names[k]); + if (i->empty) + pr_cont("empty"); + else + pr_cont("%c%u %u%c", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); + pr_cont(" -> "); +#endif + changed = snd_interval_refine(i, constrs_interval(constrs, k)); +#ifdef RULES_DEBUG + if (i->empty) + pr_cont("empty\n"); + else + pr_cont("%c%u %u%c\n", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); +#endif + if (changed) + params->cmask |= 1 << k; + if (changed < 0) + return changed; + } + + for (k = 0; k < constrs->rules_num; k++) + rstamps[k] = 0; + for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) + vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; + do { + again = 0; + for (k = 0; k < constrs->rules_num; k++) { + struct snd_pcm_hw_rule *r = &constrs->rules[k]; + unsigned int d; + int doit = 0; + if (r->cond && !(r->cond & params->flags)) + continue; + for (d = 0; r->deps[d] >= 0; d++) { + if (vstamps[r->deps[d]] > rstamps[k]) { + doit = 1; + break; + } + } + if (!doit) + continue; +#ifdef RULES_DEBUG + pr_debug("Rule %d [%p]: ", k, r->func); + if (r->var >= 0) { + pr_cont("%s = ", snd_pcm_hw_param_names[r->var]); + if (hw_is_mask(r->var)) { + m = hw_param_mask(params, r->var); + pr_cont("%x", *m->bits); + } else { + i = hw_param_interval(params, r->var); + if (i->empty) + pr_cont("empty"); + else + pr_cont("%c%u %u%c", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); + } + } +#endif + changed = r->func(params, r); +#ifdef RULES_DEBUG + if (r->var >= 0) { + pr_cont(" -> "); + if (hw_is_mask(r->var)) + pr_cont("%x", *m->bits); + else { + if (i->empty) + pr_cont("empty"); + else + pr_cont("%c%u %u%c", + i->openmin ? '(' : '[', i->min, + i->max, i->openmax ? ')' : ']'); + } + } + pr_cont("\n"); +#endif + rstamps[k] = stamp; + if (changed && r->var >= 0) { + params->cmask |= (1 << r->var); + vstamps[r->var] = stamp; + again = 1; + } + if (changed < 0) + return changed; + stamp++; + } + } while (again); + if (!params->msbits) { + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + if (snd_interval_single(i)) + params->msbits = snd_interval_value(i); + } + + if (!params->rate_den) { + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + if (snd_interval_single(i)) { + params->rate_num = snd_interval_value(i); + params->rate_den = 1; + } + } + + hw = &substream->runtime->hw; + if (!params->info) { + params->info = hw->info & ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES | + SNDRV_PCM_INFO_DRAIN_TRIGGER); + if (!hw_support_mmap(substream)) + params->info &= ~(SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID); + } + if (!params->fifo_size) { + m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + if (snd_mask_min(m) == snd_mask_max(m) && + snd_interval_min(i) == snd_interval_max(i)) { + changed = substream->ops->ioctl(substream, + SNDRV_PCM_IOCTL1_FIFO_SIZE, params); + if (changed < 0) + return changed; + } + } + params->rmask = 0; + return 0; +} + +EXPORT_SYMBOL(snd_pcm_hw_refine); + +static int snd_pcm_hw_rule_mul(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval t; + snd_interval_mul(hw_param_interval_c(params, rule->deps[0]), + hw_param_interval_c(params, rule->deps[1]), &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_div(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval t; + snd_interval_div(hw_param_interval_c(params, rule->deps[0]), + hw_param_interval_c(params, rule->deps[1]), &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_muldivk(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval t; + snd_interval_muldivk(hw_param_interval_c(params, rule->deps[0]), + hw_param_interval_c(params, rule->deps[1]), + (unsigned long) rule->private, &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_mulkdiv(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval t; + snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]), + (unsigned long) rule->private, + hw_param_interval_c(params, rule->deps[1]), &t); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + unsigned int k; + struct snd_interval *i = hw_param_interval(params, rule->deps[0]); + struct snd_mask m; + struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + snd_mask_any(&m); + for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { + int bits; + if (! snd_mask_test(mask, k)) + continue; + bits = snd_pcm_format_physical_width(k); + if (bits <= 0) + continue; /* ignore invalid formats */ + if ((unsigned)bits < i->min || (unsigned)bits > i->max) + snd_mask_reset(&m, k); + } + return snd_mask_refine(mask, &m); +} + +static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval t; + unsigned int k; + t.min = UINT_MAX; + t.max = 0; + t.openmin = 0; + t.openmax = 0; + for (k = 0; k <= SNDRV_PCM_FORMAT_LAST; ++k) { + int bits; + if (! snd_mask_test(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), k)) + continue; + bits = snd_pcm_format_physical_width(k); + if (bits <= 0) + continue; /* ignore invalid formats */ + if (t.min > (unsigned)bits) + t.min = bits; + if (t.max < (unsigned)bits) + t.max = bits; + } + t.integer = 1; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int snd_pcm_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_hardware *hw = rule->private; + return snd_interval_list(hw_param_interval(params, rule->var), + snd_pcm_known_rates.count, + snd_pcm_known_rates.list, hw->rates); +} + +static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval t; + struct snd_pcm_substream *substream = rule->private; + t.min = 0; + t.max = substream->buffer_bytes_max; + t.openmin = 0; + t.openmax = 0; + t.integer = 1; + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; + int k, err; + + for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { + snd_mask_any(constrs_mask(constrs, k)); + } + + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { + snd_interval_any(constrs_interval(constrs, k)); + } + + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_CHANNELS)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_SIZE)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)); + snd_interval_setinteger(constrs_interval(constrs, SNDRV_PCM_HW_PARAM_FRAME_BITS)); + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + snd_pcm_hw_rule_format, NULL, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + snd_pcm_hw_rule_sample_bits, NULL, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + snd_pcm_hw_rule_div, NULL, + SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, + snd_pcm_hw_rule_mul, NULL, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FRAME_BITS, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_pcm_hw_rule_div, NULL, + SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_TIME, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_BUFFER_TIME, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS, + snd_pcm_hw_rule_div, NULL, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_rule_div, NULL, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + snd_pcm_hw_rule_muldivk, (void*) 1000000, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + snd_pcm_hw_rule_mul, NULL, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_PERIODS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + snd_pcm_hw_rule_mulkdiv, (void*) 8, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + snd_pcm_hw_rule_muldivk, (void*) 1000000, + SNDRV_PCM_HW_PARAM_BUFFER_TIME, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + snd_pcm_hw_rule_muldivk, (void*) 8, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + snd_pcm_hw_rule_muldivk, (void*) 8, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_FRAME_BITS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_TIME, + snd_pcm_hw_rule_mulkdiv, (void*) 1000000, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + return 0; +} + +int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_hardware *hw = &runtime->hw; + int err; + unsigned int mask = 0; + + if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; + if (hw_support_mmap(substream)) { + if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) + mask |= 1 << SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED; + if (hw->info & SNDRV_PCM_INFO_COMPLEX) + mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX; + } + err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask); + if (err < 0) + return err; + + err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats); + if (err < 0) + return err; + + err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD); + if (err < 0) + return err; + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, + hw->channels_min, hw->channels_max); + if (err < 0) + return err; + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE, + hw->rate_min, hw->rate_max); + if (err < 0) + return err; + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + hw->period_bytes_min, hw->period_bytes_max); + if (err < 0) + return err; + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS, + hw->periods_min, hw->periods_max); + if (err < 0) + return err; + + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + hw->period_bytes_min, hw->buffer_bytes_max); + if (err < 0) + return err; + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + snd_pcm_hw_rule_buffer_bytes_max, substream, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1); + if (err < 0) + return err; + + /* FIXME: remove */ + if (runtime->dma_bytes) { + err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes); + if (err < 0) + return err; + } + + if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) { + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_rate, hw, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + } + + /* FIXME: this belong to lowlevel */ + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + + return 0; +} + +static inline unsigned int div32(unsigned int a, unsigned int b, + unsigned int *r) +{ + if (b == 0) { + *r = 0; + return UINT_MAX; + } + *r = a % b; + return a / b; +} + +static inline unsigned int div_down(unsigned int a, unsigned int b) +{ + if (b == 0) + return UINT_MAX; + return a / b; +} + +static inline unsigned int div_up(unsigned int a, unsigned int b) +{ + unsigned int r; + unsigned int q; + if (b == 0) + return UINT_MAX; + q = div32(a, b, &r); + if (r) + ++q; + return q; +} + +static inline unsigned int mul(unsigned int a, unsigned int b) +{ + if (a == 0) + return 0; + if (div_down(UINT_MAX, a) < b) + return UINT_MAX; + return a * b; +} + +static inline unsigned int muldiv32(unsigned int a, unsigned int b, + unsigned int c, unsigned int *r) +{ + u_int64_t n = (u_int64_t) a * b; + if (c == 0) { + snd_BUG_ON(!n); + *r = 0; + return UINT_MAX; + } + n = div_u64_rem(n, c, r); + if (n >= UINT_MAX) { + *r = 0; + return UINT_MAX; + } + return n; +} + +/** + * snd_interval_refine - refine the interval value of configurator + * @i: the interval value to refine + * @v: the interval value to refer to + * + * Refines the interval value with the reference value. + * The interval is changed to the range satisfying both intervals. + * The interval status (min, max, integer, etc.) are evaluated. + * + * 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) +{ + int changed = 0; + if (snd_BUG_ON(snd_interval_empty(i))) + return -EINVAL; + if (i->min < v->min) { + i->min = v->min; + i->openmin = v->openmin; + changed = 1; + } else if (i->min == v->min && !i->openmin && v->openmin) { + i->openmin = 1; + changed = 1; + } + if (i->max > v->max) { + i->max = v->max; + i->openmax = v->openmax; + changed = 1; + } else if (i->max == v->max && !i->openmax && v->openmax) { + i->openmax = 1; + changed = 1; + } + if (!i->integer && v->integer) { + i->integer = 1; + changed = 1; + } + if (i->integer) { + if (i->openmin) { + i->min++; + i->openmin = 0; + } + if (i->openmax) { + i->max--; + i->openmax = 0; + } + } else if (!i->openmin && !i->openmax && i->min == i->max) + i->integer = 1; + if (snd_interval_checkempty(i)) { + snd_interval_none(i); + return -EINVAL; + } + return changed; +} + +EXPORT_SYMBOL(snd_interval_refine); + +void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) +{ + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = mul(a->min, b->min); + c->openmin = (a->openmin || b->openmin); + c->max = mul(a->max, b->max); + c->openmax = (a->openmax || b->openmax); + c->integer = (a->integer && b->integer); +} + +/** + * snd_interval_div - refine the interval value with division + * @a: dividend + * @b: divisor + * @c: quotient + * + * c = a / b + * + * Returns non-zero if the value is changed, zero if not changed. + */ +void snd_interval_div(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) +{ + unsigned int r; + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = div32(a->min, b->max, &r); + c->openmin = (r || a->openmin || b->openmax); + if (b->min > 0) { + c->max = div32(a->max, b->min, &r); + if (r) { + c->max++; + c->openmax = 1; + } else + c->openmax = (a->openmax || b->openmin); + } else { + c->max = UINT_MAX; + c->openmax = 0; + } + c->integer = 0; +} + +/** + * snd_interval_muldivk - refine the interval value + * @a: dividend 1 + * @b: dividend 2 + * @k: divisor (as integer) + * @c: result + * + * c = a * b / k + * + * Returns non-zero if the value is changed, zero if not changed. + */ +void snd_interval_muldivk(const struct snd_interval *a, const struct snd_interval *b, + unsigned int k, struct snd_interval *c) +{ + unsigned int r; + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = muldiv32(a->min, b->min, k, &r); + c->openmin = (r || a->openmin || b->openmin); + c->max = muldiv32(a->max, b->max, k, &r); + if (r) { + c->max++; + c->openmax = 1; + } else + c->openmax = (a->openmax || b->openmax); + c->integer = 0; +} + +/** + * snd_interval_mulkdiv - refine the interval value + * @a: dividend 1 + * @k: dividend 2 (as integer) + * @b: divisor + * @c: result + * + * c = a * k / b + * + * Returns non-zero if the value is changed, zero if not changed. + */ +void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, + const struct snd_interval *b, struct snd_interval *c) +{ + unsigned int r; + if (a->empty || b->empty) { + snd_interval_none(c); + return; + } + c->empty = 0; + c->min = muldiv32(a->min, k, b->max, &r); + c->openmin = (r || a->openmin || b->openmax); + if (b->min > 0) { + c->max = muldiv32(a->max, k, b->min, &r); + if (r) { + c->max++; + c->openmax = 1; + } else + c->openmax = (a->openmax || b->openmin); + } else { + c->max = UINT_MAX; + c->openmax = 0; + } + c->integer = 0; +} + +/* ---- */ + + +/** + * snd_interval_ratnum - refine the interval value + * @i: interval to refine + * @rats_count: number of ratnum_t + * @rats: ratnum_t array + * @nump: pointer to store the resultant numerator + * @denp: pointer to store the resultant denominator + * + * 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, + unsigned int *nump, unsigned int *denp) +{ + unsigned int best_num, best_den; + int best_diff; + unsigned int k; + struct snd_interval t; + int err; + unsigned int result_num, result_den; + int result_diff; + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num = rats[k].num; + unsigned int den; + unsigned int q = i->min; + int diff; + if (q == 0) + q = 1; + den = div_up(num, q); + if (den < rats[k].den_min) + continue; + if (den > rats[k].den_max) + den = rats[k].den_max; + else { + unsigned int r; + r = (den - rats[k].den_min) % rats[k].den_step; + if (r != 0) + den -= r; + } + diff = num - q * den; + if (diff < 0) + diff = -diff; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.min = div_down(best_num, best_den); + t.openmin = !!(best_num % best_den); + + result_num = best_num; + result_diff = best_diff; + result_den = best_den; + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num = rats[k].num; + unsigned int den; + unsigned int q = i->max; + int diff; + if (q == 0) { + i->empty = 1; + return -EINVAL; + } + den = div_down(num, q); + if (den > rats[k].den_max) + continue; + if (den < rats[k].den_min) + den = rats[k].den_min; + else { + unsigned int r; + r = (den - rats[k].den_min) % rats[k].den_step; + if (r != 0) + den += rats[k].den_step - r; + } + diff = q * den - num; + if (diff < 0) + diff = -diff; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.max = div_up(best_num, best_den); + t.openmax = !!(best_num % best_den); + t.integer = 0; + err = snd_interval_refine(i, &t); + if (err < 0) + return err; + + if (snd_interval_single(i)) { + if (best_diff * result_den < result_diff * best_den) { + result_num = best_num; + result_den = best_den; + } + if (nump) + *nump = result_num; + if (denp) + *denp = result_den; + } + return err; +} + +EXPORT_SYMBOL(snd_interval_ratnum); + +/** + * snd_interval_ratden - refine the interval value + * @i: interval to refine + * @rats_count: number of struct ratden + * @rats: struct ratden array + * @nump: pointer to store the resultant numerator + * @denp: pointer to store the resultant denominator + * + * 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, + unsigned int *nump, unsigned int *denp) +{ + unsigned int best_num, best_diff, best_den; + unsigned int k; + struct snd_interval t; + int err; + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num; + unsigned int den = rats[k].den; + unsigned int q = i->min; + int diff; + num = mul(q, den); + if (num > rats[k].num_max) + continue; + if (num < rats[k].num_min) + num = rats[k].num_max; + else { + unsigned int r; + r = (num - rats[k].num_min) % rats[k].num_step; + if (r != 0) + num += rats[k].num_step - r; + } + diff = num - q * den; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.min = div_down(best_num, best_den); + t.openmin = !!(best_num % best_den); + + best_num = best_den = best_diff = 0; + for (k = 0; k < rats_count; ++k) { + unsigned int num; + unsigned int den = rats[k].den; + unsigned int q = i->max; + int diff; + num = mul(q, den); + if (num < rats[k].num_min) + continue; + if (num > rats[k].num_max) + num = rats[k].num_max; + else { + unsigned int r; + r = (num - rats[k].num_min) % rats[k].num_step; + if (r != 0) + num -= r; + } + diff = q * den - num; + if (best_num == 0 || + diff * best_den < best_diff * den) { + best_diff = diff; + best_den = den; + best_num = num; + } + } + if (best_den == 0) { + i->empty = 1; + return -EINVAL; + } + t.max = div_up(best_num, best_den); + t.openmax = !!(best_num % best_den); + t.integer = 0; + err = snd_interval_refine(i, &t); + if (err < 0) + return err; + + if (snd_interval_single(i)) { + if (nump) + *nump = best_num; + if (denp) + *denp = best_den; + } + return err; +} + +/** + * snd_interval_list - refine the interval value from the list + * @i: the interval value to refine + * @count: the number of elements in the list + * @list: the value list + * @mask: the bit-mask to evaluate + * + * Refines the interval value from the list. + * When mask is non-zero, only the elements corresponding to bit 1 are + * evaluated. + * + * 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) +{ + unsigned int k; + struct snd_interval list_range; + + if (!count) { + i->empty = 1; + return -EINVAL; + } + snd_interval_any(&list_range); + list_range.min = UINT_MAX; + list_range.max = 0; + for (k = 0; k < count; k++) { + if (mask && !(mask & (1 << k))) + continue; + if (!snd_interval_test(i, list[k])) + continue; + list_range.min = min(list_range.min, list[k]); + list_range.max = max(list_range.max, list[k]); + } + return snd_interval_refine(i, &list_range); +} + +EXPORT_SYMBOL(snd_interval_list); + +/** + * snd_interval_ranges - refine the interval value from the list of ranges + * @i: the interval value to refine + * @count: the number of elements in the list of ranges + * @ranges: the ranges list + * @mask: the bit-mask to evaluate + * + * Refines the interval value from the list of ranges. + * When mask is non-zero, only the elements corresponding to bit 1 are + * evaluated. + * + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. + */ +int snd_interval_ranges(struct snd_interval *i, unsigned int count, + const struct snd_interval *ranges, unsigned int mask) +{ + unsigned int k; + struct snd_interval range_union; + struct snd_interval range; + + if (!count) { + snd_interval_none(i); + return -EINVAL; + } + snd_interval_any(&range_union); + range_union.min = UINT_MAX; + range_union.max = 0; + for (k = 0; k < count; k++) { + if (mask && !(mask & (1 << k))) + continue; + snd_interval_copy(&range, &ranges[k]); + if (snd_interval_refine(&range, i) < 0) + continue; + if (snd_interval_empty(&range)) + continue; + + if (range.min < range_union.min) { + range_union.min = range.min; + range_union.openmin = 1; + } + if (range.min == range_union.min && !range.openmin) + range_union.openmin = 0; + if (range.max > range_union.max) { + range_union.max = range.max; + range_union.openmax = 1; + } + if (range.max == range_union.max && !range.openmax) + range_union.openmax = 0; + } + return snd_interval_refine(i, &range_union); +} +EXPORT_SYMBOL(snd_interval_ranges); + +static int snd_interval_step(struct snd_interval *i, unsigned int step) +{ + unsigned int n; + int changed = 0; + n = i->min % step; + if (n != 0 || i->openmin) { + i->min += step - n; + i->openmin = 0; + changed = 1; + } + n = i->max % step; + if (n != 0 || i->openmax) { + i->max -= n; + i->openmax = 0; + changed = 1; + } + if (snd_interval_checkempty(i)) { + i->empty = 1; + return -EINVAL; + } + return changed; +} + +/* Info constraints helpers */ + +/** + * snd_pcm_hw_rule_add - add the hw-constraint rule + * @runtime: the pcm runtime instance + * @cond: condition bits + * @var: the variable to evaluate + * @func: the evaluation function + * @private: the private data pointer passed to function + * @dep: the dependent variables + * + * 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, + snd_pcm_hw_rule_func_t func, void *private, + int dep, ...) +{ + struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; + struct snd_pcm_hw_rule *c; + unsigned int k; + va_list args; + va_start(args, dep); + if (constrs->rules_num >= constrs->rules_all) { + struct snd_pcm_hw_rule *new; + unsigned int new_rules = constrs->rules_all + 16; + new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL); + if (!new) { + va_end(args); + return -ENOMEM; + } + if (constrs->rules) { + memcpy(new, constrs->rules, + constrs->rules_num * sizeof(*c)); + kfree(constrs->rules); + } + constrs->rules = new; + constrs->rules_all = new_rules; + } + c = &constrs->rules[constrs->rules_num]; + c->cond = cond; + c->func = func; + c->var = var; + c->private = private; + k = 0; + while (1) { + if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) { + va_end(args); + return -EINVAL; + } + c->deps[k++] = dep; + if (dep < 0) + break; + dep = va_arg(args, int); + } + constrs->rules_num++; + va_end(args); + return 0; +} + +EXPORT_SYMBOL(snd_pcm_hw_rule_add); + +/** + * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint + * @runtime: PCM runtime instance + * @var: hw_params variable to apply the mask + * @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) +{ + struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; + struct snd_mask *maskp = constrs_mask(constrs, var); + *maskp->bits &= mask; + memset(maskp->bits + 1, 0, (SNDRV_MASK_MAX-32) / 8); /* clear rest */ + if (*maskp->bits == 0) + return -EINVAL; + return 0; +} + +/** + * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint + * @runtime: PCM runtime instance + * @var: hw_params variable to apply the mask + * @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) +{ + struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; + struct snd_mask *maskp = constrs_mask(constrs, var); + maskp->bits[0] &= (u_int32_t)mask; + maskp->bits[1] &= (u_int32_t)(mask >> 32); + memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */ + if (! maskp->bits[0] && ! maskp->bits[1]) + return -EINVAL; + return 0; +} +EXPORT_SYMBOL(snd_pcm_hw_constraint_mask64); + +/** + * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval + * @runtime: PCM runtime instance + * @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) +{ + struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; + return snd_interval_setinteger(constrs_interval(constrs, var)); +} + +EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); + +/** + * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval + * @runtime: PCM runtime instance + * @var: hw_params variable to apply the range + * @min: the minimal value + * @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) +{ + struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; + struct snd_interval t; + t.min = min; + t.max = max; + t.openmin = t.openmax = 0; + t.integer = 0; + return snd_interval_refine(constrs_interval(constrs, var), &t); +} + +EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); + +static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_hw_constraint_list *list = rule->private; + return snd_interval_list(hw_param_interval(params, rule->var), list->count, list->list, list->mask); +} + + +/** + * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter + * @runtime: PCM runtime instance + * @cond: condition bits + * @var: hw_params variable to apply the list constraint + * @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, + snd_pcm_hw_param_t var, + const struct snd_pcm_hw_constraint_list *l) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_list, (void *)l, + var, -1); +} + +EXPORT_SYMBOL(snd_pcm_hw_constraint_list); + +static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_hw_constraint_ranges *r = rule->private; + return snd_interval_ranges(hw_param_interval(params, rule->var), + r->count, r->ranges, r->mask); +} + + +/** + * snd_pcm_hw_constraint_ranges - apply list of range constraints to a parameter + * @runtime: PCM runtime instance + * @cond: condition bits + * @var: hw_params variable to apply the list of range constraints + * @r: ranges + * + * Apply the list of range constraints to an interval parameter. + * + * Return: Zero if successful, or a negative error code on failure. + */ +int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, + unsigned int cond, + snd_pcm_hw_param_t var, + const struct snd_pcm_hw_constraint_ranges *r) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_ranges, (void *)r, + var, -1); +} +EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges); + +static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_hw_constraint_ratnums *r = rule->private; + unsigned int num = 0, den = 0; + int err; + err = snd_interval_ratnum(hw_param_interval(params, rule->var), + r->nrats, r->rats, &num, &den); + if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { + params->rate_num = num; + params->rate_den = den; + } + return err; +} + +/** + * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter + * @runtime: PCM runtime instance + * @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, + snd_pcm_hw_param_t var, + struct snd_pcm_hw_constraint_ratnums *r) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_ratnums, r, + var, -1); +} + +EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); + +static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_hw_constraint_ratdens *r = rule->private; + unsigned int num = 0, den = 0; + int err = snd_interval_ratden(hw_param_interval(params, rule->var), + r->nrats, r->rats, &num, &den); + if (err >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { + params->rate_num = num; + params->rate_den = den; + } + return err; +} + +/** + * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter + * @runtime: PCM runtime instance + * @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, + snd_pcm_hw_param_t var, + struct snd_pcm_hw_constraint_ratdens *r) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_ratdens, r, + var, -1); +} + +EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); + +static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + unsigned int l = (unsigned long) rule->private; + int width = l & 0xffff; + unsigned int msbits = l >> 16; + struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + + if (!snd_interval_single(i)) + return 0; + + if ((snd_interval_value(i) == width) || + (width == 0 && snd_interval_value(i) > msbits)) + params->msbits = min_not_zero(params->msbits, msbits); + + return 0; +} + +/** + * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule + * @runtime: PCM runtime instance + * @cond: condition bits + * @width: sample bits width + * @msbits: msbits width + * + * This constraint will set the number of most significant bits (msbits) if a + * sample format with the specified width has been select. If width is set to 0 + * the msbits will be set for any sample format with a width larger than the + * specified msbits. + * + * 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, + unsigned int width, + unsigned int msbits) +{ + unsigned long l = (msbits << 16) | width; + return snd_pcm_hw_rule_add(runtime, cond, -1, + snd_pcm_hw_rule_msbits, + (void*) l, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); +} + +EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); + +static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + unsigned long step = (unsigned long) rule->private; + return snd_interval_step(hw_param_interval(params, rule->var), step); +} + +/** + * snd_pcm_hw_constraint_step - add a hw constraint step rule + * @runtime: PCM runtime instance + * @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, + snd_pcm_hw_param_t var, + unsigned long step) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_step, (void *) step, + var, -1); +} + +EXPORT_SYMBOL(snd_pcm_hw_constraint_step); + +static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) +{ + static unsigned int pow2_sizes[] = { + 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, + 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, + 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, + 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30 + }; + return snd_interval_list(hw_param_interval(params, rule->var), + ARRAY_SIZE(pow2_sizes), pow2_sizes, 0); +} + +/** + * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule + * @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, + snd_pcm_hw_param_t var) +{ + return snd_pcm_hw_rule_add(runtime, cond, var, + snd_pcm_hw_rule_pow2, NULL, + var, -1); +} + +EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); + +static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + unsigned int base_rate = (unsigned int)(uintptr_t)rule->private; + struct snd_interval *rate; + + rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + return snd_interval_list(rate, 1, &base_rate, 0); +} + +/** + * 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) +{ + return snd_pcm_hw_rule_add(runtime, SNDRV_PCM_HW_PARAMS_NORESAMPLE, + SNDRV_PCM_HW_PARAM_RATE, + snd_pcm_hw_rule_noresample_func, + (void *)(uintptr_t)base_rate, + SNDRV_PCM_HW_PARAM_RATE, -1); +} +EXPORT_SYMBOL(snd_pcm_hw_rule_noresample); + +
On Thu, Jun 04, 2015 at 10:47:45PM +0800, Jie Yang wrote:
The change moves refinement functions from original pcm_native.c and pcm_lib.c, to pcm_refine.c(similarly the declaration from pcm.h to pcm_refine.h), so it is not so big as it looks like.
I'd suggest that for review it'd be easier to have one patch for the move and another for the change in behaviour: it's a bit hard to find the actual change in what the kernel does in this diff and that's the bit that needs most careful consideration.
git format-patch -M may also help.
-----Original Message----- From: Mark Brown [mailto:broonie@kernel.org] Sent: Friday, June 05, 2015 3:16 AM To: Jie, Yang Cc: tiwai@suse.de; alsa-devel@alsa-project.org; Girdwood, Liam R; Zhang, Vivian Subject: Re: [RFC PATCH] ALSA: split params refinement to seperated file
On Thu, Jun 04, 2015 at 10:47:45PM +0800, Jie Yang wrote:
The change moves refinement functions from original pcm_native.c and pcm_lib.c, to pcm_refine.c(similarly the declaration from pcm.h to pcm_refine.h), so it is not so big as it looks like.
I'd suggest that for review it'd be easier to have one patch for the move and another for the change in behaviour: it's a bit hard to find the actual change in what the kernel does in this diff and that's the bit that needs most careful consideration.
git format-patch -M may also help.
Umm, Liam also suggested that, it's really hard for reviewing, but I found that "git format-patch -M" doesn't help for partially splitting/moving from a file to another/new file, I even tried '-M5%', but no luck. :(
I can't find good way to make it better, any suggestion?
~Keyon
At Mon, 8 Jun 2015 07:35:17 +0000, Jie, Yang wrote:
-----Original Message----- From: Mark Brown [mailto:broonie@kernel.org] Sent: Friday, June 05, 2015 3:16 AM To: Jie, Yang Cc: tiwai@suse.de; alsa-devel@alsa-project.org; Girdwood, Liam R; Zhang, Vivian Subject: Re: [RFC PATCH] ALSA: split params refinement to seperated file
On Thu, Jun 04, 2015 at 10:47:45PM +0800, Jie Yang wrote:
The change moves refinement functions from original pcm_native.c and pcm_lib.c, to pcm_refine.c(similarly the declaration from pcm.h to pcm_refine.h), so it is not so big as it looks like.
I'd suggest that for review it'd be easier to have one patch for the move and another for the change in behaviour: it's a bit hard to find the actual change in what the kernel does in this diff and that's the bit that needs most careful consideration.
git format-patch -M may also help.
Umm, Liam also suggested that, it's really hard for reviewing, but I found that "git format-patch -M" doesn't help for partially splitting/moving from a file to another/new file, I even tried '-M5%', but no luck. :(
I can't find good way to make it better, any suggestion?
Just begin with adding ifdefs instead of reshuffling the code. This will make easier to understand what you're going to change. At least, it's good enough for RFC. After that, we can refactor the code.
But, before going further, I'd like to know the exact measurement you've done, the target and the goal. Measuring only the static code size doesn't help much from the whole system POV. Also, it's not clear which functionality has to be kept with which drivers and which configuration.
Removing the hw constraints refinement is rather a radical change. It's a part of the PCM core functionality, and removing it means that the driver has no ways to refuse the incompatible parameters even if it knows it's broken. So, IMO, this is the very last resort. We can start from ripping off lower hanging fruits.
thanks,
Takashi
On Mon, Jun 08, 2015 at 11:21:17AM +0200, Takashi Iwai wrote:
Just begin with adding ifdefs instead of reshuffling the code. This will make easier to understand what you're going to change. At least, it's good enough for RFC. After that, we can refactor the code.
Plus if the patches get applied it at least makes it easier for people who do decide to do such major surgery.
Removing the hw constraints refinement is rather a radical change. It's a part of the PCM core functionality, and removing it means that the driver has no ways to refuse the incompatible parameters even if it knows it's broken. So, IMO, this is the very last resort. We can start from ripping off lower hanging fruits.
Indeed, part of the reason I was asking about the split was to help me figure out what takes the place of constraints - sounds like nothing? I wonder if we could have cards support this by providing fixed configurations that we could check against using less code or something. But like you say, we *must* have some other room for improvement.
At Tue, 9 Jun 2015 18:57:11 +0100, Mark Brown wrote:
On Mon, Jun 08, 2015 at 11:21:17AM +0200, Takashi Iwai wrote:
Just begin with adding ifdefs instead of reshuffling the code. This will make easier to understand what you're going to change. At least, it's good enough for RFC. After that, we can refactor the code.
Plus if the patches get applied it at least makes it easier for people who do decide to do such major surgery.
Removing the hw constraints refinement is rather a radical change. It's a part of the PCM core functionality, and removing it means that the driver has no ways to refuse the incompatible parameters even if it knows it's broken. So, IMO, this is the very last resort. We can start from ripping off lower hanging fruits.
Indeed, part of the reason I was asking about the split was to help me figure out what takes the place of constraints - sounds like nothing? I wonder if we could have cards support this by providing fixed configurations that we could check against using less code or something.
I think it's possible to reduce the hw refine part. But, the question is whether a kernel driver should trust user-space blindly. The removal of *full* hw constraints means that, and I'm worried by lack of safety.
But like you say, we *must* have some other room for improvement.
Yes, let's see other things we can cut off. For example, the PCM timer is easy to trim. It's used by dmix, but most apps / backends don't need it at all. Then the whole ALSA timer component can be removed, too.
Takashi
On Tue, Jun 09, 2015 at 08:50:50PM +0200, Takashi Iwai wrote:
Mark Brown wrote:
Indeed, part of the reason I was asking about the split was to help me figure out what takes the place of constraints - sounds like nothing? I wonder if we could have cards support this by providing fixed configurations that we could check against using less code or something.
I think it's possible to reduce the hw refine part. But, the question is whether a kernel driver should trust user-space blindly. The removal of *full* hw constraints means that, and I'm worried by lack of safety.
Quite, I think you're right on the blind trust. It seems like too much potential for people to break their systems. The code reorganisation to make it easier to modify the kernel if you really know what you're doing seems much less worrying but that final step with no further safeguards is a bit too much.
-----Original Message----- From: Takashi Iwai [mailto:tiwai@suse.de] Sent: Wednesday, June 10, 2015 2:51 AM To: Mark Brown Cc: Jie, Yang; alsa-devel@alsa-project.org; Girdwood, Liam R; Zhang, Vivian Subject: Re: [RFC PATCH] ALSA: split params refinement to seperated file
At Tue, 9 Jun 2015 18:57:11 +0100, Mark Brown wrote:
On Mon, Jun 08, 2015 at 11:21:17AM +0200, Takashi Iwai wrote:
Just begin with adding ifdefs instead of reshuffling the code. This will make easier to understand what you're going to change. At least, it's good enough for RFC. After that, we can refactor the code.
Plus if the patches get applied it at least makes it easier for people who do decide to do such major surgery.
Removing the hw constraints refinement is rather a radical change. It's a part of the PCM core functionality, and removing it means that the driver has no ways to refuse the incompatible parameters even if it knows it's broken. So, IMO, this is the very last resort. We can start from ripping off lower hanging fruits.
Indeed, part of the reason I was asking about the split was to help me figure out what takes the place of constraints - sounds like nothing? I wonder if we could have cards support this by providing fixed configurations that we could check against using less code or something.
I think it's possible to reduce the hw refine part. But, the question is whether a kernel driver should trust user-space blindly. The removal of *full* hw constraints means that, and I'm worried by lack of safety.
But like you say, we *must* have some other room for improvement.
Yes, let's see other things we can cut off. For example, the PCM timer is easy to trim. It's used by dmix, but most apps / backends don't need it at all. Then the whole ALSA timer component can be removed, too.
Thanks for pointing out that, we will investigate cutting off the PCM timer part.
~Keyon
Takashi
-----Original Message----- From: Takashi Iwai [mailto:tiwai@suse.de] Sent: Monday, June 08, 2015 5:21 PM To: Jie, Yang Cc: Mark Brown; alsa-devel@alsa-project.org; Girdwood, Liam R; Zhang, Vivian Subject: Re: [RFC PATCH] ALSA: split params refinement to seperated file
At Mon, 8 Jun 2015 07:35:17 +0000, Jie, Yang wrote:
-----Original Message----- From: Mark Brown [mailto:broonie@kernel.org] Sent: Friday, June 05, 2015 3:16 AM To: Jie, Yang Cc: tiwai@suse.de; alsa-devel@alsa-project.org; Girdwood, Liam R; Zhang, Vivian Subject: Re: [RFC PATCH] ALSA: split params refinement to seperated file
On Thu, Jun 04, 2015 at 10:47:45PM +0800, Jie Yang wrote:
The change moves refinement functions from original pcm_native.c and pcm_lib.c, to pcm_refine.c(similarly the declaration from pcm.h to pcm_refine.h), so it is not so big as it looks like.
I'd suggest that for review it'd be easier to have one patch for the move and another for the change in behaviour: it's a bit hard to find the actual change in what the kernel does in this diff and that's the bit that needs most careful consideration.
git format-patch -M may also help.
Umm, Liam also suggested that, it's really hard for reviewing, but I found that "git format-patch -M" doesn't help for partially splitting/moving from a file to another/new file, I even tried '-M5%', but no luck. :(
I can't find good way to make it better, any suggestion?
Just begin with adding ifdefs instead of reshuffling the code. This will make easier to understand what you're going to change. At least, it's good enough for RFC. After that, we can refactor the code.
That make sense.
But, before going further, I'd like to know the exact measurement you've done, the target and the goal. Measuring only the static code size doesn't
Our target the minimum memory footprint usage for ALSA on small(e.g. IoT) devices, and using 'lsmod' to check the status(including memory footprint) of the modules.
help much from the whole system POV. Also, it's not clear which functionality has to be kept with which drivers and which configuration.
We suppose the bspoke application will pass down the specific params to ALSA on those small devices, then hw refinement may be not needed.
We also made adapted change in tinyplay, did some testing and verified it works in this case.
Removing the hw constraints refinement is rather a radical change. It's a part of the PCM core functionality, and removing it means that the driver has no ways to refuse the incompatible parameters even if it knows
Can we just return error and refuse doing stream playback/capture when we know it's broken?
it's broken. So, IMO, this is the very last resort. We can start from ripping off lower hanging fruits.
Agree that it is very last resort, I realized that it is a radical change and impact a lot before sending this RFC. :|
thanks, ~Keyon
thanks,
Takashi
On Mon, Jun 08, 2015 at 07:35:17AM +0000, Jie, Yang wrote:
I'd suggest that for review it'd be easier to have one patch for the move and another for the change in behaviour: it's a bit hard to find the actual change in what the kernel does in this diff and that's the bit that needs most careful consideration.
git format-patch -M may also help.
Umm, Liam also suggested that, it's really hard for reviewing, but I found that "git format-patch -M" doesn't help for partially splitting/moving from a file to another/new file, I even tried '-M5%', but no luck. :(
I can't find good way to make it better, any suggestion?
I think the thing I suggested about having one patch that only moves code around and another that allows you to disable the constraining would also help - the first patch will be big but mechanical which helps.
participants (4)
-
Jie Yang
-
Jie, Yang
-
Mark Brown
-
Takashi Iwai