Alsa-devel
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
June 2015
- 153 participants
- 318 discussions
12 Jun '15
Hi,
We are using a SGTL5000 on a custom board, and right now, it is a 4.0.5 kernel.
When we record on the MIC in a pop is heard at the very beginning.
Is it a known issue ?
Thanks,
JM
1
0
[alsa-devel] [PATCH 0/2] genalloc: rename dev_get_gen_pool() and of_get_named_gen_pool()
by Vladimir Zapolskiy 12 Jun '15
by Vladimir Zapolskiy 12 Jun '15
12 Jun '15
Trivial nonfunctional change initially based on discussion
https://lkml.org/lkml/2015/6/8/588
Worth to mention that instead of the assumed new name
dev_gen_pool_get(), this change attempts to be more close to other
in-kernel interfaces and new function name is just gen_pool_get().
The change is based and tested on linux-next.
Vladimir Zapolskiy (2):
genalloc: rename dev_get_gen_pool() to gen_pool_get()
genalloc: rename of_get_named_gen_pool() to of_gen_pool_get()
arch/arm/mach-at91/pm.c | 2 +-
arch/arm/mach-imx/pm-imx5.c | 2 +-
arch/arm/mach-imx/pm-imx6.c | 2 +-
drivers/dma/mmp_tdma.c | 2 +-
drivers/media/platform/coda/coda-common.c | 4 ++--
include/linux/genalloc.h | 6 +++---
lib/genalloc.c | 14 +++++++-------
sound/core/memalloc.c | 2 +-
8 files changed, 17 insertions(+), 17 deletions(-)
--
2.1.4
2
2
12 Jun '15
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(a)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(a)perex.cz>
+ * Abramo Bagnara <abramo(a)alsa-project.org>
+ * Jie Yang <yang.jie(a)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(a)perex.cz>
+ * Abramo Bagnara <abramo(a)alsa-project.org>
+ * Jie Yang <yang.jie(a)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);
+
+
--
1.9.1
4
9
This imx-wm8960 device-tree-only machine driver works with sai driver
and have below feature.
* support codec master and slave mode
* support headphone jack detect
* support headphone and micphone jack event
* support asrc<->sai<->wm8960 mode
Signed-off-by: Zidan Wang <zidan.wang(a)freescale.com>
---
.../devicetree/bindings/sound/imx-audio-wm8960.txt | 68 ++
sound/soc/fsl/Kconfig | 13 +
sound/soc/fsl/Makefile | 2 +
sound/soc/fsl/imx-wm8960.c | 711 +++++++++++++++++++++
4 files changed, 794 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-wm8960.txt
create mode 100644 sound/soc/fsl/imx-wm8960.c
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-wm8960.txt b/Documentation/devicetree/bindings/sound/imx-audio-wm8960.txt
new file mode 100644
index 0000000..300d027
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/imx-audio-wm8960.txt
@@ -0,0 +1,68 @@
+Freescale i.MX audio complex with WM8960 codec
+
+Required properties:
+
+ - compatible : "fsl,imx-audio-wm8960"
+
+ - model : The user-visible name of this sound complex
+
+ - audio-codec : The phandle of the WM8960 audio codec
+
+ - hp-det : ADCLRC/GPIO1, LINPUT3/JD2 and RINPUT3/JD3 pins can
+ be selected as headphone jack detect inputs to
+ automatically disable the speaker output and enable
+ the headphone output.
+ hp-det = <hp-det-pin hp-det-polarity>;
+ hp-det-pin = 1: ADCLRC/GPIO1 used as detect pin
+ hp-det-pin = 2: LINPUT3/JD2 used as detect pin
+ hp-det-pin = 3: RINPUT3/JD3 used as detect pin
+ hp-det-polarity = 0: hp detect high for headphone
+ hp-det-polarity = 1: hp detect high for speaker
+
+ - codec-master : If "codec-master" present, codec works as master.
+ Otherwise, codec works as slave.
+
+ - audio-routing : A list of the connections between audio components.
+ Each entry is a pair of strings, the first being the
+ connection's sink, the second being the connection's
+ source. Valid names could be power supplies, WM8962
+ pins, and the jacks on the board:
+
+ Power supplies:
+ * MICB
+
+ Board connectors:
+ * Hp MIC
+ * Main MIC
+ * Headset Jack
+ * Ext Spk
+
+Example:
+
+sound {
+ compatible = "fsl,imx6ul-evk-wm8960",
+ "fsl,imx-audio-wm8960";
+ model = "wm8960-audio";
+ cpu-dai = <&sai2>;
+ audio-codec = <&codec>;
+ asrc-controller = <&asrc>;
+ codec-master;
+ hp-det = <3 0>;
+ audio-routing =
+ "Headset Jack", "HP_L",
+ "Headset Jack", "HP_R",
+ "Ext Spk", "SPK_LP",
+ "Ext Spk", "SPK_LN",
+ "Ext Spk", "SPK_RP",
+ "Ext Spk", "SPK_RN",
+ "LINPUT2", "Hp MIC",
+ "LINPUT3", "Hp MIC",
+ "RINPUT1", "Main MIC",
+ "RINPUT2", "Main MIC",
+ "Hp MIC", "MICB",
+ "Main MIC", "MICB",
+ "CPU-Playback", "ASRC-Playback",
+ "Playback", "CPU-Playback",
+ "ASRC-Capture", "CPU-Capture",
+ "CPU-Capture", "Capture";
+};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 19c302b..0d9aa56 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -229,6 +229,19 @@ config SND_SOC_EUKREA_TLV320
Enable I2S based access to the TLV320AIC23B codec attached
to the SSI interface
+config SND_SOC_IMX_WM8960
+ tristate "SoC Audio support for i.MX boards with wm8960"
+ depends on OF && I2C
+ select SND_SOC_WM8960
+ select SND_SOC_IMX_PCM_DMA
+ select SND_SOC_FSL_SAI
+ select SND_SOC_FSL_UTILS
+ select SND_KCTL_JACK
+ help
+ SoC Audio support for i.MX boards with WM8960
+ Say Y if you want to add support for SoC audio on an i.MX board with
+ a wm8960 codec.
+
config SND_SOC_IMX_WM8962
tristate "SoC Audio support for i.MX boards with wm8962"
depends on OF && I2C && INPUT
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index d28dc25..84a9a10 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -54,6 +54,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
snd-soc-wm1133-ev1-objs := wm1133-ev1.o
snd-soc-imx-es8328-objs := imx-es8328.o
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
+snd-soc-imx-wm8960-objs := imx-wm8960.o
snd-soc-imx-wm8962-objs := imx-wm8962.o
snd-soc-imx-spdif-objs := imx-spdif.o
snd-soc-imx-mc13783-objs := imx-mc13783.o
@@ -64,6 +65,7 @@ obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
+obj-$(CONFIG_SND_SOC_IMX_WM8960) += snd-soc-imx-wm8960.o
obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
diff --git a/sound/soc/fsl/imx-wm8960.c b/sound/soc/fsl/imx-wm8960.c
new file mode 100644
index 0000000..ddd17cb
--- /dev/null
+++ b/sound/soc/fsl/imx-wm8960.c
@@ -0,0 +1,711 @@
+/*
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/control.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/mfd/syscon.h>
+#include "../codecs/wm8960.h"
+
+#define DAI_NAME_SIZE 32
+
+struct imx_wm8960_data {
+ struct snd_soc_card card;
+ char codec_dai_name[DAI_NAME_SIZE];
+ char platform_name[DAI_NAME_SIZE];
+ struct clk *codec_clk;
+ unsigned int clk_frequency;
+ bool is_codec_master;
+ bool is_stream_in_use[2];
+ struct regmap *gpr;
+ unsigned int hp_det[2];
+ u32 asrc_rate;
+ u32 asrc_format;
+};
+
+struct imx_priv {
+ int hp_set_gpio;
+ int hp_active_low;
+ struct snd_kcontrol *headset_kctl;
+ struct snd_soc_codec *codec;
+ struct platform_device *pdev;
+ struct platform_device *asrc_pdev;
+ struct snd_card *snd_card;
+};
+
+static struct imx_priv card_priv;
+
+static struct snd_soc_jack imx_hp_set;
+static struct snd_soc_jack_pin imx_hp_set_pins[] = {
+ {
+ .pin = "Headset Jack",
+ .mask = SND_JACK_HEADSET,
+ },
+};
+static struct snd_soc_jack_gpio imx_hp_set_gpio = {
+ .name = "headset detect",
+ .report = SND_JACK_HEADSET,
+ .debounce_time = 250,
+ .invert = 1,
+};
+
+static int hp_set_status_check(void *data)
+{
+ struct imx_priv *priv = &card_priv;
+ struct platform_device *pdev = priv->pdev;
+ char *envp[3], *buf;
+ int hp_status, ret;
+
+ if (!gpio_is_valid(priv->hp_set_gpio))
+ return 0;
+
+ hp_status = gpio_get_value(priv->hp_set_gpio) ? 1 : 0;
+ buf = kmalloc(32, GFP_ATOMIC);
+ if (!buf) {
+ dev_err(&pdev->dev, "%s kmalloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ if (hp_status != priv->hp_active_low) {
+ snprintf(buf, 32, "STATE=%d", 2);
+ snd_soc_dapm_disable_pin(&priv->codec->dapm, "Ext Spk");
+ snd_soc_dapm_disable_pin(&priv->codec->dapm, "Main MIC");
+ ret = imx_hp_set_gpio.report;
+
+ /*
+ * As the hp MIC only connect the input for left channel, we
+ * need to route it for right channel.
+ */
+ snd_soc_update_bits(priv->codec, WM8960_ADDCTL1, 3<<2, 1<<2);
+
+ snd_kctl_jack_report(priv->snd_card, priv->headset_kctl, 1);
+ } else {
+ snprintf(buf, 32, "STATE=%d", 0);
+ snd_soc_dapm_enable_pin(&priv->codec->dapm, "Ext Spk");
+ snd_soc_dapm_enable_pin(&priv->codec->dapm, "Main MIC");
+ ret = 0;
+
+ /*
+ * As the Main MIC only connect the input for right channel,
+ * we need to route it for left channel.
+ */
+ snd_soc_update_bits(priv->codec, WM8960_ADDCTL1, 3<<2, 2<<2);
+
+ snd_kctl_jack_report(priv->snd_card, priv->headset_kctl, 0);
+ }
+
+ envp[0] = "NAME=headset";
+ envp[1] = buf;
+ envp[2] = NULL;
+ kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp);
+ kfree(buf);
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget imx_wm8960_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headset Jack", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_MIC("Hp MIC", NULL),
+ SND_SOC_DAPM_MIC("Main MIC", NULL),
+};
+
+static int imx_wm8960_gpio_init(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct imx_priv *priv = &card_priv;
+ int ret;
+
+ priv->codec = codec;
+
+ if (gpio_is_valid(priv->hp_set_gpio)) {
+ imx_hp_set_gpio.gpio = priv->hp_set_gpio;
+ imx_hp_set_gpio.jack_status_check = hp_set_status_check;
+
+ ret = snd_soc_card_jack_new(card, "Headset Jack",
+ SND_JACK_HEADSET, &imx_hp_set,
+ imx_hp_set_pins, ARRAY_SIZE(imx_hp_set_pins));
+ if (ret)
+ return ret;
+ ret = snd_soc_jack_add_gpios(&imx_hp_set, 1,
+ &imx_hp_set_gpio);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static ssize_t show_headphone(struct device_driver *dev, char *buf)
+{
+ struct imx_priv *priv = &card_priv;
+ int hp_status;
+
+ if (!gpio_is_valid(priv->hp_set_gpio)) {
+ strcpy(buf, "no detect gpio connected\n");
+ return strlen(buf);
+ }
+
+ /* Check if headphone is plugged in */
+ hp_status = gpio_get_value(priv->hp_set_gpio) ? 1 : 0;
+
+ if (hp_status != priv->hp_active_low)
+ strcpy(buf, "Headphone\n");
+ else
+ strcpy(buf, "Speaker\n");
+
+ return strlen(buf);
+}
+
+static ssize_t show_micphone(struct device_driver *dev, char *buf)
+{
+ struct imx_priv *priv = &card_priv;
+ int hp_status;
+
+ if (!gpio_is_valid(priv->hp_set_gpio)) {
+ strcpy(buf, "no detect gpio connected\n");
+ return strlen(buf);
+ }
+
+ /* Check if headphone is plugged in */
+ hp_status = gpio_get_value(priv->hp_set_gpio) ? 1 : 0;
+
+ if (hp_status != priv->hp_active_low)
+ strcpy(buf, "Hp MIC\n");
+ else
+ strcpy(buf, "Main MIC\n");
+
+ return strlen(buf);
+}
+static DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL);
+static DRIVER_ATTR(micphone, S_IRUGO | S_IWUSR, show_micphone, NULL);
+
+static void wm8960_init(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct imx_wm8960_data *data = snd_soc_card_get_drvdata(card);
+
+ /*
+ * codec ADCLRC pin configured as GPIO, DACLRC pin is used as
+ * a frame clock for ADCs and DACs
+ */
+ snd_soc_update_bits(codec, WM8960_IFACE2, 1<<6, 1<<6);
+
+ /* GPIO1 used as headphone detect output */
+ snd_soc_update_bits(codec, WM8960_ADDCTL4, 7<<4, 3<<4);
+
+ /* Enable headphone jack detect */
+ snd_soc_update_bits(codec, WM8960_ADDCTL2, 1<<6, 1<<6);
+ snd_soc_update_bits(codec, WM8960_ADDCTL2, 1<<5, data->hp_det[1]<<5);
+ snd_soc_update_bits(codec, WM8960_ADDCTL4, 3<<2, data->hp_det[0]<<2);
+ snd_soc_update_bits(codec, WM8960_ADDCTL1, 3, 3);
+
+ /* route ADC left channel to right channel in default */
+ snd_soc_update_bits(codec, WM8960_ADDCTL1, 3<<2, 1<<2);
+}
+
+/* -1 for reserved value */
+static const int sysclk_divs[] = { 1, -1, 2, -1 };
+
+/* Multiply 256 for internal 256 div */
+static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 };
+
+/* Multiply 10 to eliminate decimials */
+static const int bclk_divs[] = {
+ 10, 15, 20, 30, 40, 55, 60, 80, 110,
+ 120, 160, 220, 240, 320, 320, 320
+};
+
+static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_card *card = rtd->card;
+ struct imx_wm8960_data *data = snd_soc_card_get_drvdata(card);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct device *dev = card->dev;
+ unsigned int sample_rate = params_rate(params);
+ unsigned int sysclk, pll_in, pll_out;
+ int i, j, k, ret = 0;
+ int bclk = snd_soc_params_to_bclk(params);
+ unsigned int fmt, plln;
+
+ if (params_channels(params) == 1)
+ bclk *= 2;
+
+ data->is_stream_in_use[tx] = true;
+
+ if (data->is_stream_in_use[!tx])
+ return 0;
+
+ if (data->is_codec_master)
+ fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+ else
+ fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS;
+
+ /* set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret) {
+ dev_err(dev, "failed to set cpu dai fmt: %d\n", ret);
+ return ret;
+ }
+ /* set codec DAI configuration */
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret) {
+ dev_err(dev, "failed to set codec dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (!data->is_codec_master) {
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai,
+ 0, 0, 2, params_width(params));
+ if (ret) {
+ dev_err(dev,
+ "failed to set cpu dai tdm slot: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_OUT);
+ if (ret) {
+ dev_err(dev, "failed to set cpu sysclk: %d\n", ret);
+ return ret;
+ }
+ return 0;
+ } else {
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, 0, SND_SOC_CLOCK_IN);
+ if (ret) {
+ dev_err(dev, "failed to set cpu sysclk: %d\n", ret);
+ return ret;
+ }
+ }
+
+ data->clk_frequency = clk_get_rate(data->codec_clk);
+
+ /* Use MCLK to provide sysclk directly */
+ sysclk = data->clk_frequency;
+
+ for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+ if (sysclk_divs[i] == -1)
+ continue;
+ sysclk /= sysclk_divs[i];
+ for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+ if (sysclk == sample_rate * dac_divs[j]) {
+ for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k)
+ if (sysclk == bclk * bclk_divs[k] / 10)
+ break;
+ if (k != ARRAY_SIZE(bclk_divs))
+ break;
+ }
+ }
+ if (j != ARRAY_SIZE(dac_divs))
+ break;
+ }
+ if (i != ARRAY_SIZE(sysclk_divs)) {
+ /* Set codec sysclk */
+ snd_soc_dai_set_sysclk(codec_dai,
+ WM8960_SYSCLK_MCLK, sysclk, 0);
+ snd_soc_dai_set_clkdiv(codec_dai, WM8960_SYSCLKDIV, i << 1);
+ return 0;
+ }
+
+ /* Use PLL to provide sysclk */
+ pll_in = data->clk_frequency;
+
+ for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+ if (sysclk_divs[i] == -1)
+ continue;
+ for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+ sysclk = sample_rate * dac_divs[j];
+ pll_out = sysclk * sysclk_divs[i];
+ /*
+ * we should make sure 5 < plln < 13, or we can't get
+ * such pll_out.
+ */
+ plln = pll_out * 4 / pll_in;
+ if (plln < 6)
+ plln *= 2;
+ if ((plln < 6) || (plln > 12))
+ continue;
+ for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
+ if (sysclk == bclk * bclk_divs[k] / 10) {
+ /* Set codec pll */
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ 0, data->clk_frequency,
+ pll_out);
+ if (ret != 0)
+ continue;
+ else
+ break;
+ }
+ }
+ if (k != ARRAY_SIZE(bclk_divs))
+ break;
+ }
+ if (j != ARRAY_SIZE(dac_divs))
+ break;
+ }
+ if (i != ARRAY_SIZE(sysclk_divs)) {
+ /* Set codec sysclk */
+ snd_soc_dai_set_sysclk(codec_dai, WM8960_SYSCLK_PLL, sysclk, 0);
+ snd_soc_dai_set_clkdiv(codec_dai, WM8960_SYSCLKDIV, i << 1);
+ return 0;
+ }
+
+ dev_err(dev, "failed to configure system clock");
+ return -EINVAL;
+}
+
+static int imx_hifi_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_card *card = rtd->card;
+ struct imx_wm8960_data *data = snd_soc_card_get_drvdata(card);
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+ data->is_stream_in_use[tx] = false;
+
+ /* Power down PLL to save power*/
+ if (data->is_codec_master && !data->is_stream_in_use[!tx])
+ snd_soc_dai_set_pll(codec_dai, 0, 0, 0, 0);
+
+ return 0;
+}
+
+static u32 imx_wm8960_rates[] = { 8000, 16000, 32000, 48000 };
+static struct snd_pcm_hw_constraint_list imx_wm8960_rate_constraints = {
+ .count = ARRAY_SIZE(imx_wm8960_rates),
+ .list = imx_wm8960_rates,
+};
+
+static int imx_hifi_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct imx_wm8960_data *data = snd_soc_card_get_drvdata(card);
+ int ret = 0;
+
+ if (!data->is_codec_master) {
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &imx_wm8960_rate_constraints);
+ if (ret)
+ return ret;
+ }
+
+ /* codec mclk should be enabled early to avoid jack detect error */
+ ret = clk_prepare_enable(data->codec_clk);
+ if (ret) {
+ dev_err(card->dev, "Failed to enable MCLK: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static void imx_hifi_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct imx_wm8960_data *data = snd_soc_card_get_drvdata(card);
+
+ clk_disable_unprepare(data->codec_clk);
+}
+
+static struct snd_soc_ops imx_hifi_ops = {
+ .hw_params = imx_hifi_hw_params,
+ .hw_free = imx_hifi_hw_free,
+ .startup = imx_hifi_startup,
+ .shutdown = imx_hifi_shutdown,
+};
+
+static int imx_wm8960_late_probe(struct snd_soc_card *card)
+{
+ struct imx_wm8960_data *data = snd_soc_card_get_drvdata(card);
+
+ /* set SAI2_MCLK_DIR to enable codec MCLK */
+ if (data->gpr)
+ regmap_update_bits(data->gpr, 4, 1<<20, 1<<20);
+
+ wm8960_init(card);
+ return 0;
+}
+
+static int be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct imx_wm8960_data *data = snd_soc_card_get_drvdata(card);
+ struct imx_priv *priv = &card_priv;
+ struct snd_interval *rate;
+ struct snd_mask *mask;
+
+ if (!priv->asrc_pdev)
+ return -EINVAL;
+
+ rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ rate->max = rate->min = data->asrc_rate;
+
+ mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ snd_mask_none(mask);
+ snd_mask_set(mask, data->asrc_format);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link imx_wm8960_dai[] = {
+ {
+ .name = "HiFi",
+ .stream_name = "HiFi",
+ .codec_dai_name = "wm8960-hifi",
+ .ops = &imx_hifi_ops,
+ },
+ {
+ .name = "HiFi-ASRC-FE",
+ .stream_name = "HiFi-ASRC-FE",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .dynamic = 1,
+ .ignore_pmdown_time = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "HiFi-ASRC-BE",
+ .stream_name = "HiFi-ASRC-BE",
+ .codec_dai_name = "wm8960-hifi",
+ .platform_name = "snd-soc-dummy",
+ .no_pcm = 1,
+ .ignore_pmdown_time = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &imx_hifi_ops,
+ .be_hw_params_fixup = be_hw_params_fixup,
+ },
+};
+
+static int imx_wm8960_probe(struct platform_device *pdev)
+{
+ struct device_node *cpu_np, *codec_np, *gpr_np;
+ struct platform_device *cpu_pdev;
+ struct imx_priv *priv = &card_priv;
+ struct i2c_client *codec_dev;
+ struct imx_wm8960_data *data;
+ struct platform_device *asrc_pdev = NULL;
+ struct device_node *asrc_np;
+ u32 width;
+ int ret;
+
+ priv->pdev = pdev;
+
+ cpu_np = of_parse_phandle(pdev->dev.of_node, "cpu-dai", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "cpu dai phandle missing or invalid\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "phandle missing or invalid\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ cpu_pdev = of_find_device_by_node(cpu_np);
+ if (!cpu_pdev) {
+ dev_err(&pdev->dev, "failed to find SAI platform device\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ codec_dev = of_find_i2c_device_by_node(codec_np);
+ if (!codec_dev || !codec_dev->dev.driver) {
+ dev_err(&pdev->dev, "failed to find codec platform device\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ if (of_property_read_bool(pdev->dev.of_node, "codec-master"))
+ data->is_codec_master = true;
+
+ data->codec_clk = devm_clk_get(&codec_dev->dev, "mclk");
+ if (IS_ERR(data->codec_clk)) {
+ ret = PTR_ERR(data->codec_clk);
+ dev_err(&pdev->dev, "failed to get codec clk: %d\n", ret);
+ goto fail;
+ }
+
+ gpr_np = of_parse_phandle(pdev->dev.of_node, "gpr", 0);
+ if (gpr_np) {
+ data->gpr = syscon_node_to_regmap(gpr_np);
+ if (IS_ERR(data->gpr)) {
+ ret = PTR_ERR(data->gpr);
+ dev_err(&pdev->dev, "failed to get gpr regmap\n");
+ goto fail;
+ }
+ }
+
+ /*
+ * hp-det = <hp-det-pin hp-det-polarity>;
+ * hp-det-pin: JD1 JD2 or JD3
+ * hp-det-polarity = 0: hp detect high for headphone
+ * hp-det-polarity = 1: hp detect high for speaker
+ */
+ of_property_read_u32_array(pdev->dev.of_node,
+ "hp-det", data->hp_det, 2);
+
+ asrc_np = of_parse_phandle(pdev->dev.of_node, "asrc-controller", 0);
+ if (asrc_np) {
+ asrc_pdev = of_find_device_by_node(asrc_np);
+ priv->asrc_pdev = asrc_pdev;
+ }
+
+ data->card.dai_link = imx_wm8960_dai;
+
+ imx_wm8960_dai[0].codec_of_node = codec_np;
+ imx_wm8960_dai[0].cpu_dai_name = dev_name(&cpu_pdev->dev);
+ imx_wm8960_dai[0].platform_of_node = cpu_np;
+
+ if (!asrc_pdev) {
+ data->card.num_links = 1;
+ } else {
+ imx_wm8960_dai[1].cpu_of_node = asrc_np;
+ imx_wm8960_dai[1].platform_of_node = asrc_np;
+ imx_wm8960_dai[2].codec_of_node = codec_np;
+ imx_wm8960_dai[2].cpu_dai_name = dev_name(&cpu_pdev->dev);
+ data->card.num_links = 3;
+
+ ret = of_property_read_u32(asrc_np, "fsl,asrc-rate",
+ &data->asrc_rate);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get output rate\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = of_property_read_u32(asrc_np, "fsl,asrc-width", &width);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get output rate\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (width == 24)
+ data->asrc_format = SNDRV_PCM_FORMAT_S24_LE;
+ else
+ data->asrc_format = SNDRV_PCM_FORMAT_S16_LE;
+ }
+
+ data->card.dev = &pdev->dev;
+ ret = snd_soc_of_parse_card_name(&data->card, "model");
+ if (ret)
+ goto fail;
+ data->card.dapm_widgets = imx_wm8960_dapm_widgets;
+ data->card.num_dapm_widgets = ARRAY_SIZE(imx_wm8960_dapm_widgets);
+
+ ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
+ if (ret)
+ goto fail;
+
+ data->card.late_probe = imx_wm8960_late_probe;
+
+ platform_set_drvdata(pdev, &data->card);
+ snd_soc_card_set_drvdata(&data->card, data);
+ ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register card (%d)\n", ret);
+ goto fail;
+ }
+
+ priv->snd_card = data->card.snd_card;
+
+ priv->hp_set_gpio = of_get_named_gpio_flags(pdev->dev.of_node,
+ "hp-det-gpios", 0,
+ (enum of_gpio_flags *)&priv->hp_active_low);
+ if (IS_ERR(ERR_PTR(priv->hp_set_gpio)))
+ goto fail;
+
+ priv->headset_kctl = snd_kctl_jack_new("Headset", priv->snd_card);
+ ret = snd_ctl_add(data->card.snd_card, priv->headset_kctl);
+ if (ret)
+ goto fail;
+
+ ret = imx_wm8960_gpio_init(&data->card);
+
+ if (gpio_is_valid(priv->hp_set_gpio)) {
+ ret = driver_create_file(pdev->dev.driver,
+ &driver_attr_headphone);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "create hp attr failed (%d)\n", ret);
+ goto fail;
+ }
+ ret = driver_create_file(pdev->dev.driver,
+ &driver_attr_micphone);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "create mic attr failed (%d)\n", ret);
+ goto fail;
+ }
+ }
+fail:
+ if (cpu_np)
+ of_node_put(cpu_np);
+ if (codec_np)
+ of_node_put(codec_np);
+
+ return ret;
+}
+
+static const struct of_device_id imx_wm8960_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-wm8960", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_wm8960_dt_ids);
+
+static struct platform_driver imx_wm8960_driver = {
+ .driver = {
+ .name = "imx-wm8960",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = imx_wm8960_dt_ids,
+ },
+ .probe = imx_wm8960_probe,
+};
+module_platform_driver(imx_wm8960_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale i.MX WM8960 ASoC machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-wm8960");
--
1.9.1
3
5
Re: [alsa-devel] Can't Unload ALSA Driver Without Disconnecting the Device
by Takashi Iwai 12 Jun '15
by Takashi Iwai 12 Jun '15
12 Jun '15
At Fri, 12 Jun 2015 02:57:27 -0400,
Azizul Hakim wrote:
>
> I see. So in this scenario what'll be the way to unload the driver
> successfully? Is there a way to stop the mixer interface?
Kill pulseaudio properly. It'll auto respawn itself usually, so for
killing PA, you'd have to change the PA configuration.
Takashi
>
> On Fri, Jun 12, 2015 at 2:55 AM, Takashi Iwai <tiwai(a)suse.de> wrote:
>
> > At Fri, 12 Jun 2015 02:51:03 -0400,
> > Azizul Hakim wrote:
> > >
> > > I tried that command. The output is as below:
> > >
> > > USER PID ACCESS COMMAND
> > > /dev/snd/controlC0: mhaki005 2043 F.... pulseaudio
> > > /dev/snd/controlC1: mhaki005 2043 F.... pulseaudio
> > > /dev/snd/pcmC0D0c: mhaki005 2043 F...m pulseaudio
> > >
> > >
> > > I think pcmC0D0c stands for "Capture" device. My device is a playback
> > > device. So I don't think it is being used by anyone.
> >
> > /dev/snd/controlC1 is being used. It's the mixer interface.
> >
> >
> > Takashi
> >
> > >
> > >
> > > On Fri, Jun 12, 2015 at 2:41 AM, Takashi Iwai <tiwai(a)suse.de> wrote:
> > >
> > > > At Fri, 12 Jun 2015 02:28:16 -0400,
> > > > Azizul Hakim wrote:
> > > > >
> > > > > Hi,
> > > > >
> > > > > I've been developing a sound card driver for a USB device using
> > ALSA. My
> > > > > driver is almost working but I've got one small issue. If I try to
> > unload
> > > > > the driver module using "rmmod" command when the device is still
> > > > connected
> > > > > to the USB port, it says "Module is in use" even though I'm not
> > playing
> > > > any
> > > > > sound files or anything.
> > > > >
> > > > >
> > > > > If I try to execute the "lsmod" command I see that it is saying the
> > usage
> > > > > count of my driver to be 1, but it doesn't say anything about who is
> > > > using
> > > > > it. Once I disconnect the device from the USB port, I can easily
> > unload
> > > > the
> > > > > module.
> > > > >
> > > > >
> > > > > So it seems to me it won't be possible to unload a sound card driver
> > if
> > > > the
> > > > > card is attached in the computer. Is my assumption correct? If not,
> > what
> > > > > might be some key points to solve the issue?
> > > >
> > > > Check "fuser -v /dev/snd/*" as root. Something (e.g. a mixer
> > > > application or sound backend) must be accessing the device.
> > > >
> > > >
> > > > Takashi
> > > >
2
1
12 Jun '15
Hi,
I've been developing a sound card driver for a USB device using ALSA. My
driver is almost working but I've got one small issue. If I try to unload
the driver module using "rmmod" command when the device is still connected
to the USB port, it says "Module is in use" even though I'm not playing any
sound files or anything.
If I try to execute the "lsmod" command I see that it is saying the usage
count of my driver to be 1, but it doesn't say anything about who is using
it. Once I disconnect the device from the USB port, I can easily unload the
module.
So it seems to me it won't be possible to unload a sound card driver if the
card is attached in the computer. Is my assumption correct? If not, what
might be some key points to solve the issue?
Thanks for any suggestion
Regards
Azizul Hakim
2
4
Modify the RT5645 driver to parse platform data from device tree. This is
missing from previous patch in sound/soc/codecs/rt5645.c, that was present
in v3.
Signed-off-by: Bard Liao <bardliao(a)realtek.com>
Signed-off-by: Oder Chiou <oder_chiou(a)realtek.com>
---
sound/soc/codecs/rt5645.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 7d04b1a..7bfc17e 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -3231,6 +3231,20 @@ static struct dmi_system_id dmi_platform_intel_braswell[] = {
{ }
};
+static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
+{
+ rt5645->pdata.in2_diff = device_property_read_bool(dev,
+ "realtek,in2-differential");
+ device_property_read_u32(dev,
+ "realtek,dmic1-data-pin", &rt5645->pdata.dmic1_data_pin);
+ device_property_read_u32(dev,
+ "realtek,dmic2-data-pin", &rt5645->pdata.dmic2_data_pin);
+ device_property_read_u32(dev,
+ "realtek,jd-mode", &rt5645->pdata.jd_mode);
+
+ return 0;
+}
+
static int rt5645_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -3249,6 +3263,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
if (pdata) {
rt5645->pdata = *pdata;
+ } else if (i2c->dev.of_node) {
+ rt5645_parse_dt(rt5645, &i2c->dev);
} else {
if (dmi_check_system(dmi_platform_intel_braswell)) {
rt5645->pdata = *rt5645_pdata;
--
1.8.1.1.439.g50a6b54
1
0
[alsa-devel] [PATCH v2 - seq 1/1] ALSA seq: expose the card number of ALSA seq clients
by Adam Goode 12 Jun '15
by Adam Goode 12 Jun '15
12 Jun '15
Expose the card number of seq clients. This allows interested userspace
programs to discover the hardware device that backs a particular seq
client. Before this change, the only way to get information about the
hardware for a client was by using brittle heuristics.
SNDRV_SEQ_VERSION is not yet incremented in this patch, but should be
relatively soon.
Signed-off-by: Adam Goode <agoode(a)google.com>
diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h
index 5a5fa49..2627bee 100644
--- a/include/uapi/sound/asequencer.h
+++ b/include/uapi/sound/asequencer.h
@@ -357,7 +357,8 @@ struct snd_seq_client_info {
unsigned char event_filter[32]; /* event filter bitmap */
int num_ports; /* RO: number of ports */
int event_lost; /* number of lost events */
- char reserved[64]; /* for future use */
+ int card_number; /* RO: card number, or -1 if no card. Added in protocol version 1.0.2 */
+ char reserved[60]; /* for future use */
};
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index edbdab8..5f2e3bf 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -216,7 +216,7 @@ int __init client_init_data(void)
}
-static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
+static struct snd_seq_client *seq_create_client1(int client_index, int poolsize, int card_number)
{
unsigned long flags;
int c;
@@ -232,6 +232,7 @@ static struct snd_seq_client *seq_create_client1(int client_index, int poolsize)
return NULL;
}
client->type = NO_CLIENT;
+ client->card_number = card_number;
snd_use_lock_init(&client->use_lock);
rwlock_init(&client->ports_lock);
mutex_init(&client->ports_mutex);
@@ -327,7 +328,7 @@ static int snd_seq_open(struct inode *inode, struct file *file)
if (mutex_lock_interruptible(®ister_mutex))
return -ERESTARTSYS;
- client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS);
+ client = seq_create_client1(-1, SNDRV_SEQ_DEFAULT_EVENTS, -1);
if (client == NULL) {
mutex_unlock(®ister_mutex);
return -ENOMEM; /* failure code */
@@ -1194,6 +1195,7 @@ static void get_client_info(struct snd_seq_client *cptr,
info->event_lost = cptr->event_lost;
memcpy(info->event_filter, cptr->event_filter, 32);
info->num_ports = cptr->num_ports;
+ info->card_number = cptr->card_number;
memset(info->reserved, 0, sizeof(info->reserved));
}
@@ -2239,6 +2241,7 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
{
struct snd_seq_client *client;
va_list args;
+ int card_number = -1;
if (snd_BUG_ON(in_interrupt()))
return -EBUSY;
@@ -2252,6 +2255,7 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
return -ERESTARTSYS;
if (card) {
+ card_number = card->number;
client_index += SNDRV_SEQ_GLOBAL_CLIENTS
+ card->number * SNDRV_SEQ_CLIENTS_PER_CARD;
if (client_index >= SNDRV_SEQ_DYNAMIC_CLIENTS_BEGIN)
@@ -2259,7 +2263,7 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
}
/* empty write queue as default */
- client = seq_create_client1(client_index, 0);
+ client = seq_create_client1(client_index, 0, card_number);
if (client == NULL) {
mutex_unlock(®ister_mutex);
return -EBUSY; /* failure code */
@@ -2533,9 +2537,12 @@ void snd_seq_info_clients_read(struct snd_info_entry *entry,
continue;
}
- snd_iprintf(buffer, "Client %3d : \"%s\" [%s]\n",
+ snd_iprintf(buffer, "Client %3d : \"%s\" [%s]",
c, client->name,
client->type == USER_CLIENT ? "User" : "Kernel");
+ if (client->card_number != -1)
+ snd_iprintf(buffer, ", card %d", client->card_number);
+ snd_iprintf(buffer, "\n");
snd_seq_info_dump_ports(buffer, client);
if (snd_seq_write_pool_allocated(client)) {
snd_iprintf(buffer, " Output pool :\n");
diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h
index 20f0a72..627be9d 100644
--- a/sound/core/seq/seq_clientmgr.h
+++ b/sound/core/seq/seq_clientmgr.h
@@ -50,6 +50,7 @@ struct snd_seq_client {
accept_output: 1;
char name[64]; /* client name */
int number; /* client number */
+ int card_number; /* card number, or -1 if no card */
unsigned int filter; /* filter flags */
DECLARE_BITMAP(event_filter, 256);
snd_use_lock_t use_lock;
--
2.4.2
1
0
Re: [alsa-devel] ASoC: davinci-mcasp: Choose PCM driver based on configured DMA controller
by Dan Carpenter 11 Jun '15
by Dan Carpenter 11 Jun '15
11 Jun '15
Hello Jyri Sarha,
The patch 9fbd58cf4ab0: "ASoC: davinci-mcasp: Choose PCM driver based
on configured DMA controller" from Jun 2, 2015, leads to the
following static checker warning:
sound/soc/davinci/davinci-mcasp.c:1842 davinci_mcasp_probe()
info: ignoring unreachable code.
sound/soc/davinci/davinci-mcasp.c
1812 switch (ret) {
1813 case PCM_EDMA:
1814 #if IS_BUILTIN(CONFIG_SND_EDMA_SOC) || \
1815 (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \
1816 IS_MODULE(CONFIG_SND_EDMA_SOC))
1817 ret = edma_pcm_platform_register(&pdev->dev);
1818 #else
1819 dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n");
1820 ret = -EINVAL;
1821 goto err;
1822 #endif
1823 break;
1824 case PCM_SDMA:
1825 #if IS_BUILTIN(CONFIG_SND_OMAP_SOC) || \
1826 (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \
1827 IS_MODULE(CONFIG_SND_OMAP_SOC))
1828 ret = omap_pcm_platform_register(&pdev->dev);
1829 #else
1830 dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n");
1831 ret = -EINVAL;
1832 goto err;
1833 #endif
1834 break;
1835 default:
1836 dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret);
1837 case -EPROBE_DEFER:
1838 goto err;
1839 break;
1840 }
1841
Depending on the ifdefs then this is reachable, I guess...
1842 if (ret) {
1843 dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
1844 goto err;
1845 }
1846
1847 return 0;
1848
1849 err:
1850 pm_runtime_disable(&pdev->dev);
1851 return ret;
1852 }
1853
regards,
dan carpenter
2
1
11 Jun '15
Commit a1b3fda6ae ALSA: hdac_ext: add hdac extended controller,
erroneously added snd_hdac_ext_bus_map_codec_to_link() function
declaration, so remove it
Signed-off-by: Vinod Koul <vinod.koul(a)intel.com>
---
include/sound/hdaudio_ext.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h
index 0044caa19155..202350a8eddb 100644
--- a/include/sound/hdaudio_ext.h
+++ b/include/sound/hdaudio_ext.h
@@ -47,7 +47,6 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *chip,
bool enable, int index);
int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *bus);
-int snd_hdac_ext_bus_map_codec_to_link(struct hdac_ext_bus *bus, int addr);
struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_ext_bus *bus,
const char *codec_name);
--
1.9.1
2
1