All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/4] Initial prototype of DAPM sample rate domains
@ 2018-10-11 16:28 Charles Keepax
  2018-10-11 16:28 ` [RFC PATCH 1/4] ASoC: dapm: Add support for hw_free on CODEC to CODEC links Charles Keepax
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Charles Keepax @ 2018-10-11 16:28 UTC (permalink / raw)
  To: broonie; +Cc: patches, alsa-devel, lgirdwood

Here is a first pass at adding dapm sample rate domain
support. Things are still pretty rough in quite a few places and
I expect some major refactoring before they are ready to merge,
but things are far enough along for people to look at the approach
I am taking to the problem. And should provide enough for discussion
at the mini-conference, although hopefully I will get some more done
next week as well.

The basic high level summary is two new concepts are added to
ASoC, one being a sample rate domain and the other being a domain
group. The domain groups represent widgets that are limited by
hardware restrictions to always run at the same sample rate as each
other. Sample rate domains represent a slot on a device for a sample
rate. Both of these are currently limited to be within the scope of
a single component.  The core will track which groups are connected
together, then as a group is powered up it will be allocated a domain
based on the other groups it is connected to. Domain groups are
implemented as a new type of DAPM widget and the domains themselves
are a new thing which devices add through their component drivers.

As we have previously discussed my intention would be that we also
add bridges between domains although I haven't started work on that
yet. The current code is limited to within a single component and
I think that the bridges will allow better support for propagating
rates between components as well as obviously for SRCs. Currently
the SRCs on wm5110 are just being handled as points where the
domain groups don't connect to another peer.  This allows them to
function but doesn't accurately convey things like the limitations
which input rates support which output rates on the ISRC.

Thanks,
Charles

Charles Keepax (4):
  ASoC: dapm: Add support for hw_free on CODEC to CODEC links
  ASoC: dapm: Add support for a rate domain widget
  ASoC: domain: Add sample rate domain support
  ASoC: arizona: Add rate domain support

 include/sound/soc-dapm.h   |  14 ++
 include/sound/soc-domain.h |  98 +++++++++++
 include/sound/soc.h        |   8 +
 sound/soc/Makefile         |   2 +-
 sound/soc/codecs/arizona.c | 255 ++++++++++++++++++++++------
 sound/soc/codecs/arizona.h |  76 ++++++++-
 sound/soc/codecs/wm5110.c  | 414 ++++++++++++++++++++++++++++++++++-----------
 sound/soc/soc-core.c       |   8 +
 sound/soc/soc-dapm.c       |  57 ++++++-
 sound/soc/soc-domain.c     | 412 ++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 1183 insertions(+), 161 deletions(-)
 create mode 100644 include/sound/soc-domain.h
 create mode 100644 sound/soc/soc-domain.c

-- 
2.11.0

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 1/4] ASoC: dapm: Add support for hw_free on CODEC to CODEC links
  2018-10-11 16:28 [RFC PATCH 0/4] Initial prototype of DAPM sample rate domains Charles Keepax
@ 2018-10-11 16:28 ` Charles Keepax
  2018-10-19 12:26   ` Applied "ASoC: dapm: Add support for hw_free on CODEC to CODEC links" to the asoc tree Mark Brown
  2018-10-19 12:34   ` Mark Brown
  2018-10-11 16:28 ` [RFC PATCH 2/4] ASoC: dapm: Add support for a rate domain widget Charles Keepax
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 9+ messages in thread
From: Charles Keepax @ 2018-10-11 16:28 UTC (permalink / raw)
  To: broonie; +Cc: patches, alsa-devel, lgirdwood

Currently, on power down for a CODEC to CODEC DAI link we only call
digital_mute and shutdown. Provide a little more flexibility for drivers
by adding a call to hw_free as well.

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
---
 sound/soc/soc-dapm.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 8c5b065c88806..a5178845065b3 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -3737,25 +3737,30 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
 			ret = 0;
 		}
 
+		substream.stream = SNDRV_PCM_STREAM_CAPTURE;
 		snd_soc_dapm_widget_for_each_source_path(w, path) {
 			source = path->source->priv;
 
+			if (source->driver->ops->hw_free)
+				source->driver->ops->hw_free(&substream,
+							     source);
+
 			source->active--;
-			if (source->driver->ops->shutdown) {
-				substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+			if (source->driver->ops->shutdown)
 				source->driver->ops->shutdown(&substream,
 							      source);
-			}
 		}
 
+		substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
 		snd_soc_dapm_widget_for_each_sink_path(w, path) {
 			sink = path->sink->priv;
 
+			if (sink->driver->ops->hw_free)
+				sink->driver->ops->hw_free(&substream, sink);
+
 			sink->active--;
-			if (sink->driver->ops->shutdown) {
-				substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+			if (sink->driver->ops->shutdown)
 				sink->driver->ops->shutdown(&substream, sink);
-			}
 		}
 		break;
 
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 2/4] ASoC: dapm: Add support for a rate domain widget
  2018-10-11 16:28 [RFC PATCH 0/4] Initial prototype of DAPM sample rate domains Charles Keepax
  2018-10-11 16:28 ` [RFC PATCH 1/4] ASoC: dapm: Add support for hw_free on CODEC to CODEC links Charles Keepax
@ 2018-10-11 16:28 ` Charles Keepax
  2018-10-11 16:28 ` [RFC PATCH 3/4] ASoC: domain: Add sample rate domain support Charles Keepax
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Charles Keepax @ 2018-10-11 16:28 UTC (permalink / raw)
  To: broonie; +Cc: patches, alsa-devel, lgirdwood

Some CODECs can support multiple sample rates internally but
frequently groups of functionality will be force to the run on the
same sample rate. For example, perhaps all the DACs are required to
run at a single rate. Utimately, it would be ideal if the sample
rates could be propagated through from the top level DAI links to
these groups of widgets. This should allow more complex use-cases
involving multiple sample rates to be connected and will also allow
detection of errors where blocks with incompatible sample rates
are connected.

A first step in this process is to provide a mechanism that documents
which blocks are tidied together with respect to sample rate and
track when they are in use.  This is acheived by adding a supply
like widget that represents a group of widgets that are bound to a
single sample rate. This domain widget should be connected to all
inputs and outputs to the group of widgets, that way it will power
up whenever anything in that group of widgets does.

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
---
 include/sound/soc-dapm.h | 4 ++++
 sound/soc/soc-dapm.c     | 5 +++++
 2 files changed, 9 insertions(+)

diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index bd8163f151cb8..c0ef27b2d4b22 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -273,6 +273,9 @@ struct device;
 	.reg = SND_SOC_NOPM, .event = dapm_pinctrl_event, \
 	.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }
 
+#define SND_SOC_DAPM_RATE(wname, wreg, wshift, winvert, wops, wpriv) \
+{	.id = snd_soc_dapm_rate, .name = wname, \
+	SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) }
 
 
 /* dapm kcontrol types */
@@ -519,6 +522,7 @@ enum snd_soc_dapm_type {
 	snd_soc_dapm_asrc,		/* DSP/CODEC ASRC component */
 	snd_soc_dapm_encoder,		/* FW/SW audio encoder component */
 	snd_soc_dapm_decoder,		/* FW/SW audio decoder component */
+	snd_soc_dapm_rate,		/* Rate group */
 };
 
 enum snd_soc_dapm_subclass {
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index a5178845065b3..7e3858d1e81dc 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -68,6 +68,7 @@ static int dapm_up_seq[] = {
 	[snd_soc_dapm_regulator_supply] = 1,
 	[snd_soc_dapm_pinctrl] = 1,
 	[snd_soc_dapm_clock_supply] = 1,
+	[snd_soc_dapm_rate] = 2,
 	[snd_soc_dapm_supply] = 2,
 	[snd_soc_dapm_micbias] = 3,
 	[snd_soc_dapm_dai_link] = 2,
@@ -115,6 +116,7 @@ static int dapm_down_seq[] = {
 	[snd_soc_dapm_dai_out] = 10,
 	[snd_soc_dapm_dai_link] = 11,
 	[snd_soc_dapm_supply] = 12,
+	[snd_soc_dapm_rate] = 12,
 	[snd_soc_dapm_clock_supply] = 13,
 	[snd_soc_dapm_pinctrl] = 13,
 	[snd_soc_dapm_regulator_supply] = 13,
@@ -1912,6 +1914,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event)
 			case snd_soc_dapm_vmid:
 				break;
 			case snd_soc_dapm_supply:
+			case snd_soc_dapm_rate:
 			case snd_soc_dapm_regulator_supply:
 			case snd_soc_dapm_pinctrl:
 			case snd_soc_dapm_clock_supply:
@@ -2326,6 +2329,7 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt,
 		case snd_soc_dapm_mixer:
 		case snd_soc_dapm_mixer_named_ctl:
 		case snd_soc_dapm_supply:
+		case snd_soc_dapm_rate:
 		case snd_soc_dapm_regulator_supply:
 		case snd_soc_dapm_pinctrl:
 		case snd_soc_dapm_clock_supply:
@@ -3522,6 +3526,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
 		w->power_check = dapm_generic_check_power;
 		break;
 	case snd_soc_dapm_supply:
+	case snd_soc_dapm_rate:
 	case snd_soc_dapm_regulator_supply:
 	case snd_soc_dapm_pinctrl:
 	case snd_soc_dapm_clock_supply:
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 3/4] ASoC: domain: Add sample rate domain support
  2018-10-11 16:28 [RFC PATCH 0/4] Initial prototype of DAPM sample rate domains Charles Keepax
  2018-10-11 16:28 ` [RFC PATCH 1/4] ASoC: dapm: Add support for hw_free on CODEC to CODEC links Charles Keepax
  2018-10-11 16:28 ` [RFC PATCH 2/4] ASoC: dapm: Add support for a rate domain widget Charles Keepax
@ 2018-10-11 16:28 ` Charles Keepax
  2018-10-11 16:28 ` [RFC PATCH 4/4] ASoC: arizona: Add " Charles Keepax
  2018-10-22 14:27 ` [RFC PATCH 0/4] Initial prototype of DAPM sample rate domains Pierre-Louis Bossart
  4 siblings, 0 replies; 9+ messages in thread
From: Charles Keepax @ 2018-10-11 16:28 UTC (permalink / raw)
  To: broonie; +Cc: patches, alsa-devel, lgirdwood

The rate domain widgets allowed tracking of which hardware blocks
are physically bound to the same sample rate. The next step is to
follow which blocks are connected together as two directly connected
blocks should also run at the same rate, even though the hardware
may provide independent settings for them.

To acheive this two new concepts are introduced to ASoC, a rate
domain and a rate domain group. The rate domain group corresponds
to the rate domain widgets previously added to DAPM. And the domains
correspond to actual sample rates.

The rate domain groups internally track which other groups they
are connected to. These lists of peer groups are updated as DAPM
routes are connected/disconnected and form a collection of graphs
tracking which domain groups are connected.  Note that these graphs
are significantly smaller than the DAPM graph itself.

When a domain group's corresponding widget is powered up then
the group must locate an actual domain to attach to. Firstly, the
group will walk its peer graph, should it find it is attached to
widgets that require certain domains it will limit the choice to
those. For example if a widget is connected into a graph that is
already powered up then it will find the only suitable domain is
the one being currently used by the groups in the graph.

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
---

Thinking about trying to split this into two patches perhaps one
to add the tracking of the rate domain groups connecting together
and then a second patch to add the actual domains.

Thanks,
Charles

 include/sound/soc-dapm.h   |  12 +-
 include/sound/soc-domain.h |  98 +++++++++++
 include/sound/soc.h        |   8 +
 sound/soc/Makefile         |   2 +-
 sound/soc/soc-core.c       |   8 +
 sound/soc/soc-dapm.c       |  35 ++++
 sound/soc/soc-domain.c     | 412 +++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 573 insertions(+), 2 deletions(-)
 create mode 100644 include/sound/soc-domain.h
 create mode 100644 sound/soc/soc-domain.c

diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index c0ef27b2d4b22..c736b1d3e4931 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -275,7 +275,12 @@ struct device;
 
 #define SND_SOC_DAPM_RATE(wname, wreg, wshift, winvert, wops, wpriv) \
 {	.id = snd_soc_dapm_rate, .name = wname, \
-	SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) }
+	SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+	.event = snd_soc_domain_event, \
+	.event_flags = SND_SOC_DAPM_WILL_PMU | SND_SOC_DAPM_PRE_PMU | \
+		       SND_SOC_DAPM_POST_PMD, \
+	.priv = (&(struct snd_soc_domain_group_driver){ \
+	.name = wname, .ops = wops, .private_data = wpriv}),}
 
 
 /* dapm kcontrol types */
@@ -410,6 +415,9 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
 void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card);
 
+int snd_soc_dapm_connect_domains(struct snd_soc_dapm_context *dapm,
+				 const char * const a, const char * const b);
+
 /* dapm path setup */
 int snd_soc_dapm_new_widgets(struct snd_soc_card *card);
 void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm);
@@ -629,6 +637,8 @@ struct snd_soc_dapm_widget {
 	int endpoints[2];
 
 	struct clk *clk;
+
+	struct snd_soc_domain_group *dgroup;
 };
 
 struct snd_soc_dapm_update {
diff --git a/include/sound/soc-domain.h b/include/sound/soc-domain.h
new file mode 100644
index 0000000000000..94e1c1ae9ff00
--- /dev/null
+++ b/include/sound/soc-domain.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ASoC Sample Rate Domain Support
+ *
+ * Copyright (c) 2018 Cirrus Logic, Inc. and
+ *                    Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <ckeepax@opensource.cirrus.com>
+ */
+
+#ifndef LINUX_SND_SOC_DOMAIN_H
+#define LINUX_SND_SOC_DOMAIN_H
+
+#define SND_SOC_DOMAIN_CURRENT -1
+
+struct snd_soc_domain;
+struct snd_soc_domain_group;
+
+struct snd_soc_domain_ops {
+	int (*set_rate)(struct snd_soc_domain *domain, int rate);
+	int (*get_rate)(struct snd_soc_domain *domain);
+};
+
+struct snd_soc_domain_driver {
+	const char * const name;
+
+	const struct snd_soc_domain_ops *ops;
+
+	void *private_data;
+};
+
+struct snd_soc_domain {
+	const struct snd_soc_domain_driver *driver;
+	struct snd_soc_component *component;
+
+	/* TODO: Probably should be a snd_pcm_hw_params */
+	int rate;
+
+	int active_groups;
+};
+
+struct snd_soc_domain_group_ops {
+	int (*set_domain)(struct snd_soc_domain_group *group, int dom);
+
+	int (*mask_domains)(struct snd_soc_domain_group *group,
+			    unsigned long *domain_mask);
+	/* optional */
+	int (*pick_domain)(struct snd_soc_domain_group *group,
+			   const unsigned long *domain_mask);
+};
+
+struct snd_soc_domain_group_driver {
+	const char * const name;
+
+	const struct snd_soc_domain_group_ops *ops;
+
+	void *private_data;
+};
+
+struct snd_soc_domain_group {
+	const struct snd_soc_domain_group_driver *driver;
+	struct snd_soc_component *component;
+
+	int domain_index;
+	int attach_count;
+
+	struct list_head peers;
+
+	unsigned int walking:1;
+	unsigned int power:1;
+};
+
+int devm_snd_soc_domain_init(struct snd_soc_component *component);
+
+struct snd_soc_domain_group *
+devm_snd_soc_domain_group_new(struct snd_soc_component *component,
+			      const struct snd_soc_domain_group_driver *drv);
+
+struct snd_soc_domain *snd_soc_domain_get(struct snd_soc_domain_group *group,
+					  int index);
+bool snd_soc_domain_active(struct snd_soc_domain *domain);
+int snd_soc_domain_get_rate(struct snd_soc_domain *domain);
+
+int snd_soc_domain_set_rate(struct snd_soc_domain_group *group, int rate);
+
+/* TODO: API to force a particular domain onto a group? */
+int snd_soc_domain_attach(struct snd_soc_domain_group *group);
+int snd_soc_domain_detach(struct snd_soc_domain_group *group);
+
+int snd_soc_domain_event(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *kcontrol,
+			 int event);
+
+int snd_soc_domain_connect_widgets(struct snd_soc_dapm_widget *a,
+				   struct snd_soc_dapm_widget *b,
+				   bool connect);
+
+#endif
diff --git a/include/sound/soc.h b/include/sound/soc.h
index f1dab1f4b194d..475843a17ebd4 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -413,6 +413,7 @@ struct snd_soc_jack_pin;
 #include <sound/soc-dapm.h>
 #include <sound/soc-dpcm.h>
 #include <sound/soc-topology.h>
+#include <sound/soc-domain.h>
 
 struct snd_soc_jack_gpio;
 
@@ -763,6 +764,9 @@ struct snd_soc_component_driver {
 	const struct snd_soc_dapm_route *dapm_routes;
 	unsigned int num_dapm_routes;
 
+	const struct snd_soc_domain_driver *domains;
+	unsigned int num_domains;
+
 	int (*probe)(struct snd_soc_component *);
 	void (*remove)(struct snd_soc_component *);
 	int (*suspend)(struct snd_soc_component *);
@@ -838,6 +842,9 @@ struct snd_soc_component {
 	struct list_head dai_list;
 	int num_dai;
 
+	struct snd_soc_domain *domains;
+	int num_domains;
+
 	struct regmap *regmap;
 	int val_bytes;
 
@@ -1036,6 +1043,7 @@ struct snd_soc_card {
 
 	struct mutex mutex;
 	struct mutex dapm_mutex;
+	struct mutex domain_mutex;
 
 	bool instantiated;
 	bool topology_shortname_created;
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 62a5f87c3cfc4..185f51aa963a2 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o
-snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o
+snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o soc-domain.o
 snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o
 
 ifneq ($(CONFIG_SND_SOC_TOPOLOGY),)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 62e8e36062df0..4623adb27543b 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1345,6 +1345,13 @@ static int soc_probe_component(struct snd_soc_card *card,
 		}
 	}
 
+	ret = devm_snd_soc_domain_init(component);
+	if (ret < 0) {
+		dev_err(component->dev, "Failed to initialise domains: %d\n",
+			ret);
+		goto err_probe;
+	}
+
 	if (component->driver->controls)
 		snd_soc_add_component_controls(component,
 					       component->driver->controls,
@@ -2739,6 +2746,7 @@ int snd_soc_register_card(struct snd_soc_card *card)
 	card->instantiated = 0;
 	mutex_init(&card->mutex);
 	mutex_init(&card->dapm_mutex);
+	mutex_init(&card->domain_mutex);
 
 	return snd_soc_bind_card(card);
 }
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 7e3858d1e81dc..a3f01626fda76 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -2179,6 +2179,9 @@ static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,
 	if (path->connect == connect)
 		return;
 
+	/* TODO: Need to handle routes that are already connected */
+	snd_soc_domain_connect_widgets(path->source, path->sink, connect);
+
 	path->connect = connect;
 	dapm_mark_dirty(path->source, reason);
 	dapm_mark_dirty(path->sink, reason);
@@ -2685,6 +2688,14 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
 	if (wsource->is_supply || wsink->is_supply)
 		path->is_supply = 1;
 
+	switch (wsource->id) {
+	case snd_soc_dapm_rate:
+		wsink->dgroup = wsource->dgroup;
+		break;
+	default:
+		break;
+	}
+
 	/* connect static paths */
 	if (control == NULL) {
 		path->connect = 1;
@@ -3463,6 +3474,14 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
 			goto request_failed;
 		}
 		break;
+	case snd_soc_dapm_rate:
+		w->dgroup = devm_snd_soc_domain_group_new(dapm->component,
+							  w->priv);
+		if (IS_ERR(w->dgroup)) {
+			ret = PTR_ERR(w->dgroup);
+			goto request_failed;
+		}
+		break;
 	default:
 		break;
 	}
@@ -4566,6 +4585,22 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card)
 					    SND_SOC_BIAS_OFF);
 }
 
+int snd_soc_dapm_connect_domains(struct snd_soc_dapm_context *dapm,
+				 const char * const a, const char * const b)
+{
+	struct snd_soc_dapm_widget *wa, *wb;
+
+	wa = dapm_find_widget(dapm, a, false);
+	if (!wa)
+		return -ENODEV;
+	wb = dapm_find_widget(dapm, b, false);
+	if (!wb)
+		return -ENODEV;
+
+	return snd_soc_domain_connect_widgets(wa, wb, true);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_domains);
+
 /* Module information */
 MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
 MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
diff --git a/sound/soc/soc-domain.c b/sound/soc/soc-domain.c
new file mode 100644
index 0000000000000..01914d5971601
--- /dev/null
+++ b/sound/soc/soc-domain.c
@@ -0,0 +1,412 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ASoC Sample Rate Domain Support
+ *
+ * Copyright (c) 2018 Cirrus Logic, Inc. and
+ *                    Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <ckeepax@opensource.cirrus.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+
+struct domain_group_peer {
+	struct list_head list;
+	int link_count;
+	struct snd_soc_domain_group *group;
+};
+
+static inline void domain_mutex_lock(struct snd_soc_component *component)
+{
+	mutex_lock(&component->card->domain_mutex);
+}
+
+static inline void domain_mutex_unlock(struct snd_soc_component *component)
+{
+	mutex_unlock(&component->card->domain_mutex);
+}
+
+static inline void domain_mutex_assert_held(struct snd_soc_component *component)
+{
+	lockdep_assert_held(&component->card->domain_mutex);
+}
+
+int devm_snd_soc_domain_init(struct snd_soc_component *component)
+{
+	int i;
+
+	if (!component->driver->num_domains)
+		return 0;
+
+	component->num_domains = component->driver->num_domains;
+	component->domains = devm_kcalloc(component->card->dev,
+					  component->num_domains,
+					  sizeof(*component->domains),
+					  GFP_KERNEL);
+	if (!component->domains)
+		return -ENOMEM;
+
+	for (i = 0; i < component->num_domains; i++) {
+		component->domains[i].component = component;
+		component->domains[i].driver = &component->driver->domains[i];
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(devm_snd_soc_domain_init);
+
+struct snd_soc_domain_group *
+devm_snd_soc_domain_group_new(struct snd_soc_component *component,
+			      const struct snd_soc_domain_group_driver *driver)
+{
+	struct snd_soc_domain_group *group;
+
+	group = devm_kzalloc(component->card->dev, sizeof(*group), GFP_KERNEL);
+	if (!group)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&group->peers);
+
+	group->component = component;
+	group->driver = driver;
+
+	return group;
+}
+EXPORT_SYMBOL_GPL(devm_snd_soc_domain_group_new);
+
+struct snd_soc_domain *snd_soc_domain_get(struct snd_soc_domain_group *group,
+					  int index)
+{
+	int ndomains = group->component->num_domains;
+
+	domain_mutex_assert_held(group->component);
+
+	if (index == SND_SOC_DOMAIN_CURRENT)
+		index = group->domain_index;
+
+	if (index < 0 || index >= ndomains)
+		return NULL;
+
+	return &group->component->domains[index];
+}
+EXPORT_SYMBOL_GPL(snd_soc_domain_get);
+
+bool snd_soc_domain_active(struct snd_soc_domain *domain)
+{
+	bool active;
+
+	domain_mutex_assert_held(domain->component);
+
+	active = !!domain->active_groups;
+
+	return active;
+}
+EXPORT_SYMBOL_GPL(snd_soc_domain_active);
+
+int snd_soc_domain_get_rate(struct snd_soc_domain *domain)
+{
+	domain_mutex_assert_held(domain->component);
+
+	return domain->rate;
+}
+EXPORT_SYMBOL_GPL(snd_soc_domain_get_rate);
+
+int snd_soc_domain_set_rate(struct snd_soc_domain_group *group, int rate)
+{
+	struct snd_soc_domain *domain;
+	int ret = -ENODEV;
+
+	domain_mutex_lock(group->component);
+
+	domain = snd_soc_domain_get(group, SND_SOC_DOMAIN_CURRENT);
+	if (domain) {
+		domain->rate = rate;
+		ret = domain->driver->ops->set_rate(domain, rate);
+	}
+
+	domain_mutex_unlock(group->component);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_domain_set_rate);
+
+static struct snd_soc_domain_group *
+group_walk(struct snd_soc_domain_group *group, bool local,
+	   bool (*cond)(struct snd_soc_domain_group *g, void *c), void *cookie)
+{
+	struct domain_group_peer *link;
+	struct snd_soc_domain_group *target = NULL;
+
+	domain_mutex_assert_held(group->component);
+
+	if (group->walking)
+		return NULL;
+
+	dev_vdbg(group->component->dev, "Walking %s\n", group->driver->name);
+
+	if (cond(group, cookie))
+		return group;
+
+	group->walking = true;
+	list_for_each_entry(link, &group->peers, list) {
+		if (!link->group->power)
+			continue;
+
+		if (local && link->group->component != group->component)
+			continue;
+
+		target = group_walk(link->group, local, cond, cookie);
+		if (target)
+			break;
+	}
+	group->walking = false;
+
+	return target;
+}
+
+static bool group_mask(struct snd_soc_domain_group *group, void *cookie)
+{
+	unsigned long *mask = cookie;
+
+	if (group->attach_count)
+		*mask &= 1 << group->domain_index;
+	else if (group->driver->ops->mask_domains)
+		group->driver->ops->mask_domains(group, mask);
+
+	return false;
+}
+
+static int group_pick(struct snd_soc_domain_group *group,
+				const unsigned long *domain_mask)
+{
+	int ndomains = group->component->num_domains;
+	int i;
+
+	domain_mutex_assert_held(group->component);
+
+	for_each_set_bit(i, domain_mask, ndomains) {
+		struct snd_soc_domain *domain = &group->component->domains[i];
+
+		if (!snd_soc_domain_active(domain))
+			return i;
+	}
+
+	return find_first_bit(domain_mask, ndomains);
+}
+
+int snd_soc_domain_attach(struct snd_soc_domain_group *group)
+{
+	int ret = 0;
+
+	domain_mutex_lock(group->component);
+
+	dev_dbg(group->component->dev, "Attaching domain to %s: %d\n",
+		group->driver->name, group->attach_count);
+
+	if (!group->attach_count) {
+		const struct snd_soc_domain_group_ops *ops = group->driver->ops;
+		unsigned long dom_map = ~0UL;
+		struct snd_soc_domain *domain;
+
+		group_walk(group, true, group_mask, &dom_map);
+
+		if (ops->pick_domain)
+			group->domain_index = ops->pick_domain(group, &dom_map);
+		else
+			group->domain_index = group_pick(group, &dom_map);
+
+		domain = snd_soc_domain_get(group, SND_SOC_DOMAIN_CURRENT);
+		if (!domain) {
+			dev_err(group->component->dev,
+				"No suitable domain to attach for %s\n",
+				group->driver->name);
+			ret = -ENODEV;
+			goto error;
+		}
+
+		dev_dbg(group->component->dev, "Apply domain %s to %s\n",
+			domain->driver->name, group->driver->name);
+
+		ret = ops->set_domain(group, group->domain_index);
+		if (ret)
+			goto error;
+
+		domain->active_groups++;
+	}
+
+	group->attach_count++;
+
+error:
+	domain_mutex_unlock(group->component);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_domain_attach);
+
+int snd_soc_domain_detach(struct snd_soc_domain_group *group)
+{
+	int ret = 0;
+
+	domain_mutex_lock(group->component);
+
+	dev_dbg(group->component->dev, "Detaching domain from %s: %d\n",
+		group->driver->name, group->attach_count);
+
+	if (!group->attach_count) {
+		dev_err(group->component->dev, "Unbalanced detach on %s\n",
+			group->driver->name);
+		ret = -EPERM;
+	} else {
+		struct snd_soc_domain *domain;
+
+		domain = snd_soc_domain_get(group, SND_SOC_DOMAIN_CURRENT);
+		if (!domain) {
+			dev_err(group->component->dev,
+				"Group %s has missing domain\n",
+				group->driver->name);
+			ret = -ENODEV;
+			goto error;
+		}
+
+		domain->active_groups--;
+		group->attach_count--;
+	}
+
+error:
+	domain_mutex_unlock(group->component);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_domain_detach);
+
+int snd_soc_domain_event(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *kcontrol,
+			 int event)
+{
+	switch (event) {
+	case SND_SOC_DAPM_WILL_PMU:
+		w->dgroup->power = true;
+		return 0;
+	case SND_SOC_DAPM_PRE_PMU:
+		return snd_soc_domain_attach(w->dgroup);
+	case SND_SOC_DAPM_POST_PMD:
+		w->dgroup->power = false;
+		return snd_soc_domain_detach(w->dgroup);
+	default:
+		return 0;
+	}
+}
+EXPORT_SYMBOL_GPL(snd_soc_domain_event);
+
+static struct domain_group_peer *
+group_peer_find(struct snd_soc_domain_group *group,
+		struct snd_soc_domain_group *peer)
+{
+	struct domain_group_peer *link;
+
+	domain_mutex_assert_held(group->component);
+
+	list_for_each_entry(link, &group->peers, list) {
+		if (link->group == peer)
+			return link;
+	}
+
+	return NULL;
+}
+
+static int group_peer_new(struct snd_soc_domain_group *group,
+			  struct snd_soc_domain_group *peer)
+{
+	struct domain_group_peer *link;
+
+	domain_mutex_lock(group->component);
+
+	link = group_peer_find(group, peer);
+	if (!link) {
+		dev_dbg(group->component->dev, "New peer: %s -> %s\n",
+			group->driver->name, peer->driver->name);
+
+		link = kzalloc(sizeof(*link), GFP_KERNEL);
+		if (!link)
+			return -ENOMEM;
+
+		INIT_LIST_HEAD(&link->list);
+		link->group = peer;
+
+		list_add_tail(&link->list, &group->peers);
+	}
+
+	link->link_count++;
+
+	domain_mutex_unlock(group->component);
+
+	return 0;
+}
+
+static int group_peer_delete(struct snd_soc_domain_group *group,
+			     struct snd_soc_domain_group *peer)
+{
+	struct domain_group_peer *link;
+	int ret = 0;
+
+	domain_mutex_lock(group->component);
+
+	link = group_peer_find(group, peer);
+	if (!link) {
+		dev_err(group->component->dev,
+			"Delete on invalid peer: %s -> %s\n",
+			group->driver->name, peer->driver->name);
+		ret = -ENOENT;
+		goto error;
+	}
+
+	link->link_count--;
+	if (!link->link_count) {
+		dev_dbg(group->component->dev, "Delete peer: %s -> %s\n",
+			group->driver->name, peer->driver->name);
+
+		list_del(&link->list);
+		kfree(link);
+	}
+
+error:
+	domain_mutex_unlock(group->component);
+
+	return ret;
+}
+
+int snd_soc_domain_connect_widgets(struct snd_soc_dapm_widget *a,
+				   struct snd_soc_dapm_widget *b,
+				   bool connect)
+{
+	int (*op)(struct snd_soc_domain_group *group,
+		  struct snd_soc_domain_group *peer);
+	int ret;
+
+	if (!a->dgroup || !b->dgroup)
+		return 0;
+
+	dev_dbg(a->dapm->dev, "%s %s,%s - %s,%s\n",
+		connect ? "Connecting" : "Disconnecting",
+		a->name, a->dgroup->driver->name,
+		b->name, b->dgroup->driver->name);
+
+	if (connect)
+		op = group_peer_new;
+	else
+		op = group_peer_delete;
+
+	ret = op(a->dgroup, b->dgroup);
+	if (ret)
+		return ret;
+
+	ret = op(b->dgroup, a->dgroup);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_domain_connect_widgets);
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH 4/4] ASoC: arizona: Add rate domain support
  2018-10-11 16:28 [RFC PATCH 0/4] Initial prototype of DAPM sample rate domains Charles Keepax
                   ` (2 preceding siblings ...)
  2018-10-11 16:28 ` [RFC PATCH 3/4] ASoC: domain: Add sample rate domain support Charles Keepax
@ 2018-10-11 16:28 ` Charles Keepax
  2018-10-22 14:27 ` [RFC PATCH 0/4] Initial prototype of DAPM sample rate domains Pierre-Louis Bossart
  4 siblings, 0 replies; 9+ messages in thread
From: Charles Keepax @ 2018-10-11 16:28 UTC (permalink / raw)
  To: broonie; +Cc: patches, alsa-devel, lgirdwood

Move to using sample rate domains on the Arizona CODECs. Rate domain
group widgets are added and these are connected to all the widgets they
supply.

arizona_hw_params_rate is updated significantly, it now manually
attached the AIF sample rate domain group to a domain at the rate
requested by the params. This ensures that any widgets powering up that
are connected to the audio interface will be placed on the same sample
rate domain.

Whilst the SLIMbus technically supports a different sample rate on each
channel the driver currently only supports sample rates for capture and
playback on each DAI, as such the SLIMBus channels that are grouped onto
a single DAI are manually connected together as peers.

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
---

This one is obviously not quite finished yet, the other CODECs
in Arizona need updated and the macros that are duplicated will
obviously just be updated in that case.

Thanks,
Charles

 sound/soc/codecs/arizona.c | 255 ++++++++++++++++++++++------
 sound/soc/codecs/arizona.h |  76 ++++++++-
 sound/soc/codecs/wm5110.c  | 414 ++++++++++++++++++++++++++++++++++-----------
 3 files changed, 591 insertions(+), 154 deletions(-)

diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 5727ea079ad7a..37ce772691739 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -633,12 +633,12 @@ const char *arizona_sample_rate_val_to_name(unsigned int rate_val)
 EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name);
 
 const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
-	"SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate",
+	"SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate", "ASYNCCLK rate 2",
 };
 EXPORT_SYMBOL_GPL(arizona_rate_text);
 
 const unsigned int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
-	0, 1, 2, 8,
+	0, 1, 2, 8, 9,
 };
 EXPORT_SYMBOL_GPL(arizona_rate_val);
 
@@ -1681,40 +1681,15 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
 	struct snd_soc_component *component = dai->component;
 	struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
 	struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
-	int base = dai->driver->base;
-	int i, sr_val, ret;
-
-	/*
-	 * We will need to be more flexible than this in future,
-	 * currently we use a single sample rate for SYSCLK.
-	 */
-	for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++)
-		if (arizona_sr_vals[i] == params_rate(params))
-			break;
-	if (i == ARRAY_SIZE(arizona_sr_vals)) {
-		arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
-				params_rate(params));
-		return -EINVAL;
-	}
-	sr_val = i;
-
-	switch (priv->arizona->type) {
-	case WM5102:
-	case WM8997:
-		if (arizona_sr_vals[sr_val] >= 88200)
-			ret = arizona_dvfs_up(component, ARIZONA_DVFS_SR1_RQ);
-		else
-			ret = arizona_dvfs_down(component, ARIZONA_DVFS_SR1_RQ);
+	int ret;
+	struct snd_soc_domain_group *dgrp;
 
-		if (ret) {
-			arizona_aif_err(dai, "Failed to change DVFS %d\n", ret);
-			return ret;
-		}
-		break;
-	default:
-		break;
-	}
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dgrp = dai->playback_widget->dgroup;
+	else
+		dgrp = dai->capture_widget->dgroup;
 
+	/* TODO: This should be handled on power up of the OUT_RATE widget */
 	switch (dai_priv->clk) {
 	case ARIZONA_CLK_SYSCLK:
 		switch (priv->arizona->type) {
@@ -1725,31 +1700,38 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
 		default:
 			break;
 		}
-
-		snd_soc_component_update_bits(component, ARIZONA_SAMPLE_RATE_1,
-					      ARIZONA_SAMPLE_RATE_1_MASK,
-					      sr_val);
-		if (base)
-			snd_soc_component_update_bits(component,
-					base + ARIZONA_AIF_RATE_CTRL,
-					ARIZONA_AIF1_RATE_MASK, 0);
 		break;
 	case ARIZONA_CLK_ASYNCCLK:
-		snd_soc_component_update_bits(component,
-					      ARIZONA_ASYNC_SAMPLE_RATE_1,
-					      ARIZONA_ASYNC_SAMPLE_RATE_1_MASK,
-					      sr_val);
-		if (base)
-			snd_soc_component_update_bits(component,
-					base + ARIZONA_AIF_RATE_CTRL,
-					ARIZONA_AIF1_RATE_MASK,
-					8 << ARIZONA_AIF1_RATE_SHIFT);
 		break;
 	default:
 		arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
 		return -EINVAL;
 	}
 
+	/* TODO: Needs updated to handle multiple calls of hw_params */
+	ret = snd_soc_domain_attach(dgrp);
+	if (ret)
+		return ret;
+
+	ret = snd_soc_domain_set_rate(dgrp, params_rate(params));
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int arizona_hw_free(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	struct snd_soc_domain_group *dgrp;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dgrp = dai->playback_widget->dgroup;
+	else
+		dgrp = dai->capture_widget->dgroup;
+
+	snd_soc_domain_detach(dgrp);
+
 	return 0;
 }
 
@@ -2029,6 +2011,7 @@ const struct snd_soc_dai_ops arizona_dai_ops = {
 	.set_fmt = arizona_set_fmt,
 	.set_tdm_slot = arizona_set_tdm_slot,
 	.hw_params = arizona_hw_params,
+	.hw_free = arizona_hw_free,
 	.set_sysclk = arizona_dai_set_sysclk,
 	.set_tristate = arizona_set_tristate,
 };
@@ -2858,6 +2841,176 @@ int arizona_of_get_audio_pdata(struct arizona *arizona)
 }
 EXPORT_SYMBOL_GPL(arizona_of_get_audio_pdata);
 
+static int arizona_set_rate(struct snd_soc_domain *dom, int rate)
+{
+	struct snd_soc_component *component = dom->component;
+	struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+	const struct arizona_rate_dom_priv *dpriv = dom->driver->private_data;
+	int i, ret;
+
+	dev_dbg(arizona->dev, "Set %s to %d Hz\n", dom->driver->name, rate);
+
+	switch (arizona->type) {
+	case WM5102:
+	case WM8997:
+		if (rate >= 88200)
+			ret = arizona_dvfs_up(component, dpriv->dvfs_mask);
+		else
+			ret = arizona_dvfs_down(component, dpriv->dvfs_mask);
+
+		if (ret) {
+			dev_err(arizona->dev,
+				"Failed to change DVFS for %s: %d\n",
+				dom->driver->name, ret);
+			return ret;
+		}
+		break;
+	default:
+		break;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) {
+		if (arizona_sr_vals[i] == rate)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(arizona_sr_vals)) {
+		dev_err(arizona->dev, "Invalid sample rate: %d Hz\n", rate);
+		return -EINVAL;
+	}
+
+	return regmap_update_bits(arizona->regmap, dpriv->reg,
+				  ARIZONA_SAMPLE_RATE_1_MASK, i);
+}
+
+static int arizona_get_rate(struct snd_soc_domain *dom)
+{
+	struct snd_soc_component *component = dom->component;
+	struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+	const struct arizona_rate_dom_priv *dpriv = dom->driver->private_data;
+	unsigned int rate;
+	int ret;
+
+	ret = regmap_read(arizona->regmap, dpriv->reg, &rate);
+	if (ret)
+		return ret;
+
+	rate &= ARIZONA_SAMPLE_RATE_1_MASK;
+
+	if (rate >= ARRAY_SIZE(arizona_sr_vals)) {
+		dev_err(arizona->dev, "Read bad sample rate: 0x%x\n", rate);
+		return -EINVAL;
+	}
+
+	rate = arizona_sr_vals[rate];
+
+	dev_dbg(arizona->dev, "Got %u Hz for %s\n", rate, dom->driver->name);
+
+	return (int)rate;
+}
+
+static const struct snd_soc_domain_ops arizona_dom_ops = {
+	.set_rate = arizona_set_rate,
+	.get_rate = arizona_get_rate,
+};
+
+const struct snd_soc_domain_driver arizona_rate_domains[ARIZONA_RATE_ENUM_SIZE] = {
+	{
+		.name = "Sample Rate 1",
+		.ops = &arizona_dom_ops,
+		.private_data = &(struct arizona_rate_dom_priv){
+			.reg = ARIZONA_SAMPLE_RATE_1,
+			.val = 0,
+			.dvfs_mask = ARIZONA_DVFS_SR1_RQ,
+		},
+	},
+	{
+		.name = "Sample Rate 2",
+		.ops = &arizona_dom_ops,
+		.private_data = &(struct arizona_rate_dom_priv){
+			.reg = ARIZONA_SAMPLE_RATE_2,
+			.val = 1,
+			.dvfs_mask = ARIZONA_DVFS_SR2_RQ,
+		},
+	},
+	{
+		.name = "Sample Rate 3",
+		.ops = &arizona_dom_ops,
+		.private_data = &(struct arizona_rate_dom_priv){
+			.reg = ARIZONA_SAMPLE_RATE_3,
+			.val = 2,
+			.dvfs_mask = ARIZONA_DVFS_SR3_RQ,
+		},
+	},
+	{
+		.name = "Async Sample Rate 1",
+		.ops = &arizona_dom_ops,
+		.private_data = &(struct arizona_rate_dom_priv){
+			.reg = ARIZONA_ASYNC_SAMPLE_RATE_1,
+			.val = 8,
+			.dvfs_mask = ARIZONA_DVFS_ASR1_RQ,
+		},
+	},
+	{
+		.name = "Async Sample Rate 2",
+		.ops = &arizona_dom_ops,
+		.private_data = &(struct arizona_rate_dom_priv){
+			.reg = ARIZONA_ASYNC_SAMPLE_RATE_2,
+			.val = 9,
+			.dvfs_mask = ARIZONA_DVFS_ASR2_RQ,
+		},
+	},
+};
+
+int arizona_set_domain(struct snd_soc_domain_group *dgrp, int dom)
+{
+	struct arizona *arizona = dev_get_drvdata(dgrp->component->dev->parent);
+	const struct arizona_rate_grp_priv *gpriv = dgrp->driver->private_data;
+	const struct arizona_rate_dom_priv *dpriv = arizona_rate_domains[dom].private_data;
+
+	return regmap_update_bits(arizona->regmap, gpriv->reg, gpriv->mask,
+			dpriv->val << gpriv->shift);
+}
+
+int arizona_mask_domain(struct snd_soc_domain_group *dgrp, unsigned long *mask)
+{
+	const struct arizona_rate_grp_priv *gpriv = dgrp->driver->private_data;
+	struct snd_soc_component *component = dgrp->component;
+	struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
+	unsigned long supported = 0;
+
+	switch (gpriv->reg) {
+	case ARIZONA_AIF1_RATE_CTRL:
+	case ARIZONA_AIF2_RATE_CTRL:
+	case ARIZONA_AIF3_RATE_CTRL:
+		switch (priv->dai[ARIZONA_AIF1_RATE_CTRL - gpriv->reg].clk) {
+		case ARIZONA_CLK_ASYNCCLK:
+			supported |= 0x18;
+			break;
+		default:
+			supported |= 0x7;
+			break;
+		}
+		break;
+	default:
+		if (gpriv->sync)
+			supported |= 0x7;
+		if (gpriv->async)
+			supported |= 0x18;
+		break;
+	}
+
+	*mask &= supported;
+
+	return 0;
+}
+
+const struct snd_soc_domain_group_ops arizona_dgrp_ops = {
+	.set_domain = arizona_set_domain,
+	.mask_domains = arizona_mask_domain,
+};
+EXPORT_SYMBOL_GPL(arizona_dgrp_ops);
+
 MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index e3ccee5627c6b..734a90648224f 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -63,6 +63,10 @@
 #define ARIZONA_MAX_ADSP 4
 
 #define ARIZONA_DVFS_SR1_RQ	0x001
+#define ARIZONA_DVFS_SR2_RQ	0x002
+#define ARIZONA_DVFS_SR3_RQ	0x004
+#define ARIZONA_DVFS_ASR1_RQ	0x008
+#define ARIZONA_DVFS_ASR2_RQ	0x010
 #define ARIZONA_DVFS_ADSP1_RQ	0x100
 
 /* Notifier events */
@@ -189,6 +193,26 @@ extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
 	ARIZONA_MIXER_INPUT_ROUTES(name " Input 3"), \
 	ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
 
+#define ARIZONA_MUX_ROUTES_R(widget, name, rate) \
+	{ widget, NULL, name " Input" }, \
+	{ name " Input", NULL, rate }, \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Input")
+
+#define ARIZONA_MIXER_ROUTES_R(widget, name, rate) \
+	{ widget, NULL, name " Mixer" },         \
+	{ name " Mixer", NULL, name " Input 1" }, \
+	{ name " Mixer", NULL, name " Input 2" }, \
+	{ name " Mixer", NULL, name " Input 3" }, \
+	{ name " Mixer", NULL, name " Input 4" }, \
+	{ name " Input 1", NULL, rate }, \
+	{ name " Input 2", NULL, rate }, \
+	{ name " Input 3", NULL, rate }, \
+	{ name " Input 4", NULL, rate }, \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Input 1"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Input 2"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Input 3"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
+
 #define ARIZONA_DSP_ROUTES(name) \
 	{ name, NULL, name " Preloader"}, \
 	{ name " Preloader", NULL, "SYSCLK" }, \
@@ -208,6 +232,32 @@ extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
 	ARIZONA_MIXER_ROUTES(name, name "L"), \
 	ARIZONA_MIXER_ROUTES(name, name "R")
 
+#define ARIZONA_DSP_ROUTES_R(name, rate) \
+	{ name, NULL, rate }, \
+	{ name, NULL, name " Preloader"}, \
+	{ name " Preloader", NULL, "SYSCLK" }, \
+	{ name " Preload", NULL, name " Preloader"}, \
+	{ name, NULL, name " Aux 1" }, \
+	{ name, NULL, name " Aux 2" }, \
+	{ name, NULL, name " Aux 3" }, \
+	{ name, NULL, name " Aux 4" }, \
+	{ name, NULL, name " Aux 5" }, \
+	{ name, NULL, name " Aux 6" }, \
+	{ name " Aux 1", NULL, rate }, \
+	{ name " Aux 2", NULL, rate }, \
+	{ name " Aux 3", NULL, rate }, \
+	{ name " Aux 4", NULL, rate }, \
+	{ name " Aux 5", NULL, rate }, \
+	{ name " Aux 6", NULL, rate }, \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \
+	ARIZONA_MIXER_ROUTES_R(name, name "L", rate), \
+	ARIZONA_MIXER_ROUTES_R(name, name "R", rate)
+
 #define ARIZONA_EQ_CONTROL(xname, xbase)                      \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,   \
 	.info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
@@ -222,7 +272,7 @@ extern unsigned int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
 	((unsigned long)&(struct soc_bytes) { .base = xbase,  \
 	 .num_regs = 1 }) }
 
-#define ARIZONA_RATE_ENUM_SIZE 4
+#define ARIZONA_RATE_ENUM_SIZE 5
 #define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14
 
 extern const char * const arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
@@ -354,4 +404,28 @@ static inline int arizona_unregister_notifier(struct snd_soc_component *componen
 
 int arizona_of_get_audio_pdata(struct arizona *arizona);
 
+struct arizona_rate_dom_priv {
+	unsigned int reg;
+	unsigned int val;
+	int dvfs_mask;
+};
+
+struct arizona_rate_grp_priv {
+	unsigned int reg;
+	unsigned int mask;
+	unsigned int shift;
+
+	bool sync;
+	bool async;
+};
+
+#define ARIZONA_RATE_WIDGET(rname, rsync, rasync, rreg, rmask) \
+	SND_SOC_DAPM_RATE(rname, SND_SOC_NOPM, 0, 0, &arizona_dgrp_ops, \
+		(&(struct arizona_rate_grp_priv){ .reg = rreg, \
+		.mask = rmask##_MASK, .shift = rmask##_SHIFT, \
+		.sync = rsync, .async = rasync}))
+
+extern const struct snd_soc_domain_group_ops arizona_dgrp_ops;
+extern const struct snd_soc_domain_driver arizona_rate_domains[ARIZONA_RATE_ENUM_SIZE];
+
 #endif
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index b0789a03d699b..930092370735e 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -1092,6 +1092,88 @@ static const struct snd_kcontrol_new wm5110_output_anc_src[] = {
 };
 
 static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
+ARIZONA_RATE_WIDGET("In Rate", 1, 0, ARIZONA_INPUT_RATE, ARIZONA_IN_RATE),
+
+ARIZONA_RATE_WIDGET("Out Rate", 1, 0, ARIZONA_OUTPUT_RATE_1, ARIZONA_OUT_RATE),
+ARIZONA_RATE_WIDGET("PWM Rate", 1, 0, ARIZONA_PWM_DRIVE_1, ARIZONA_PWM_RATE),
+
+ARIZONA_RATE_WIDGET("Tone Rate", 1, 1, ARIZONA_TONE_GENERATOR_1,
+		    ARIZONA_TONE_RATE),
+ARIZONA_RATE_WIDGET("Noise Rate", 1, 1, ARIZONA_COMFORT_NOISE_GENERATOR,
+		    ARIZONA_NOISE_GEN_RATE),
+ARIZONA_RATE_WIDGET("Haptics Rate", 1, 0, ARIZONA_HAPTICS_CONTROL_1,
+		    ARIZONA_HAP_RATE),
+ARIZONA_RATE_WIDGET("FX Rate", 1, 1, ARIZONA_FX_CTRL1, ARIZONA_FX_RATE),
+
+ARIZONA_RATE_WIDGET("AIF1 Rate", 1, 1, ARIZONA_AIF1_RATE_CTRL,
+		    ARIZONA_AIF1_RATE),
+ARIZONA_RATE_WIDGET("AIF2 Rate", 1, 1, ARIZONA_AIF2_RATE_CTRL,
+		    ARIZONA_AIF2_RATE),
+ARIZONA_RATE_WIDGET("AIF3 Rate", 1, 1, ARIZONA_AIF3_RATE_CTRL,
+		    ARIZONA_AIF3_RATE),
+
+ARIZONA_RATE_WIDGET("ASRC Rate", 1, 0, ARIZONA_ASRC_RATE1, ARIZONA_ASRC_RATE1),
+ARIZONA_RATE_WIDGET("ASRC Async Rate", 0, 1, ARIZONA_ASRC_RATE2,
+		    ARIZONA_ASRC_RATE2),
+
+ARIZONA_RATE_WIDGET("Mic Mute Rate", 1, 1, ARIZONA_MIC_NOISE_MIX_CONTROL_1,
+		    ARIZONA_MICMUTE_RATE),
+
+ARIZONA_RATE_WIDGET("ISRC1 FSH", 1, 1, ARIZONA_ISRC_1_CTRL_1,
+		    ARIZONA_ISRC1_FSH),
+ARIZONA_RATE_WIDGET("ISRC1 FSL", 1, 1, ARIZONA_ISRC_1_CTRL_2,
+		    ARIZONA_ISRC1_FSL),
+ARIZONA_RATE_WIDGET("ISRC2 FSH", 1, 1, ARIZONA_ISRC_2_CTRL_1,
+		    ARIZONA_ISRC2_FSH),
+ARIZONA_RATE_WIDGET("ISRC2 FSL", 1, 1, ARIZONA_ISRC_2_CTRL_2,
+		    ARIZONA_ISRC2_FSL),
+ARIZONA_RATE_WIDGET("ISRC3 FSH", 1, 1, ARIZONA_ISRC_3_CTRL_1,
+		    ARIZONA_ISRC3_FSH),
+ARIZONA_RATE_WIDGET("ISRC3 FSL", 1, 1, ARIZONA_ISRC_3_CTRL_2,
+		    ARIZONA_ISRC3_FSL),
+
+ARIZONA_RATE_WIDGET("DSP1 Rate", 1, 1, ARIZONA_DSP1_CONTROL_1,
+		    ARIZONA_DSP1_RATE),
+ARIZONA_RATE_WIDGET("DSP2 Rate", 1, 1, ARIZONA_DSP2_CONTROL_1,
+		    ARIZONA_DSP1_RATE),
+ARIZONA_RATE_WIDGET("DSP3 Rate", 1, 1, ARIZONA_DSP3_CONTROL_1,
+		    ARIZONA_DSP1_RATE),
+ARIZONA_RATE_WIDGET("DSP4 Rate", 1, 1, ARIZONA_DSP4_CONTROL_1,
+		    ARIZONA_DSP1_RATE),
+
+ARIZONA_RATE_WIDGET("SLIMRX1 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_1,
+		    ARIZONA_SLIMRX1_RATE),
+ARIZONA_RATE_WIDGET("SLIMRX2 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_1,
+		    ARIZONA_SLIMRX2_RATE),
+ARIZONA_RATE_WIDGET("SLIMRX3 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_2,
+		    ARIZONA_SLIMRX3_RATE),
+ARIZONA_RATE_WIDGET("SLIMRX4 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_2,
+		    ARIZONA_SLIMRX4_RATE),
+ARIZONA_RATE_WIDGET("SLIMRX5 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_3,
+		    ARIZONA_SLIMRX5_RATE),
+ARIZONA_RATE_WIDGET("SLIMRX6 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_3,
+		    ARIZONA_SLIMRX6_RATE),
+ARIZONA_RATE_WIDGET("SLIMRX7 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_4,
+		    ARIZONA_SLIMRX7_RATE),
+ARIZONA_RATE_WIDGET("SLIMRX8 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_4,
+		    ARIZONA_SLIMRX8_RATE),
+ARIZONA_RATE_WIDGET("SLIMTX1 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_5,
+		    ARIZONA_SLIMTX1_RATE),
+ARIZONA_RATE_WIDGET("SLIMTX2 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_5,
+		    ARIZONA_SLIMTX2_RATE),
+ARIZONA_RATE_WIDGET("SLIMTX3 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_6,
+		    ARIZONA_SLIMTX3_RATE),
+ARIZONA_RATE_WIDGET("SLIMTX4 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_6,
+		    ARIZONA_SLIMTX4_RATE),
+ARIZONA_RATE_WIDGET("SLIMTX5 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_7,
+		    ARIZONA_SLIMTX5_RATE),
+ARIZONA_RATE_WIDGET("SLIMTX6 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_7,
+		    ARIZONA_SLIMTX6_RATE),
+ARIZONA_RATE_WIDGET("SLIMTX7 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_8,
+		    ARIZONA_SLIMTX7_RATE),
+ARIZONA_RATE_WIDGET("SLIMTX8 Rate", 1, 1, ARIZONA_SLIMBUS_RATES_8,
+		    ARIZONA_SLIMTX8_RATE),
+
 SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
 		    0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU |
 		    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
@@ -1728,6 +1810,119 @@ SND_SOC_DAPM_OUTPUT("MICSUPP"),
 	{ name, "DSP4.6", "DSP4" }
 
 static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
+	{ "IN1L PGA", NULL, "In Rate" },
+	{ "IN1R PGA", NULL, "In Rate" },
+	{ "IN2L PGA", NULL, "In Rate" },
+	{ "IN2R PGA", NULL, "In Rate" },
+	{ "IN3L PGA", NULL, "In Rate" },
+	{ "IN3R PGA", NULL, "In Rate" },
+	{ "IN4L PGA", NULL, "In Rate" },
+	{ "IN4R PGA", NULL, "In Rate" },
+
+	{ "AIF1 Playback", NULL, "AIF1 Rate" },
+	{ "AIF1 Capture", NULL, "AIF1 Rate" },
+	{ "AIF1RX1", NULL, "AIF1 Rate" },
+	{ "AIF1RX2", NULL, "AIF1 Rate" },
+	{ "AIF1RX3", NULL, "AIF1 Rate" },
+	{ "AIF1RX4", NULL, "AIF1 Rate" },
+	{ "AIF1RX5", NULL, "AIF1 Rate" },
+	{ "AIF1RX6", NULL, "AIF1 Rate" },
+	{ "AIF1RX7", NULL, "AIF1 Rate" },
+	{ "AIF1RX8", NULL, "AIF1 Rate" },
+
+	{ "AIF2 Playback", NULL, "AIF2 Rate" },
+	{ "AIF2 Capture", NULL, "AIF2 Rate" },
+	{ "AIF2RX1", NULL, "AIF2 Rate" },
+	{ "AIF2RX2", NULL, "AIF2 Rate" },
+	{ "AIF2RX3", NULL, "AIF2 Rate" },
+	{ "AIF2RX4", NULL, "AIF2 Rate" },
+	{ "AIF2RX5", NULL, "AIF2 Rate" },
+	{ "AIF2RX6", NULL, "AIF2 Rate" },
+
+	{ "AIF3 Playback", NULL, "AIF3 Rate" },
+	{ "AIF3 Capture", NULL, "AIF3 Rate" },
+	{ "AIF3RX1", NULL, "AIF3 Rate" },
+	{ "AIF3RX2", NULL, "AIF3 Rate" },
+
+	{ "EQ1", NULL, "FX Rate" },
+	{ "EQ2", NULL, "FX Rate" },
+	{ "EQ3", NULL, "FX Rate" },
+	{ "EQ4", NULL, "FX Rate" },
+
+	{ "DRC1L", NULL, "FX Rate" },
+	{ "DRC1R", NULL, "FX Rate" },
+	{ "DRC2L", NULL, "FX Rate" },
+	{ "DRC2R", NULL, "FX Rate" },
+
+	{ "LHPF1", NULL, "FX Rate" },
+	{ "LHPF2", NULL, "FX Rate" },
+	{ "LHPF3", NULL, "FX Rate" },
+	{ "LHPF4", NULL, "FX Rate" },
+
+	{ "PWM1 Driver", NULL, "PWM Rate" },
+	{ "PWM2 Driver", NULL, "PWM Rate" },
+
+	{ "HAPTICS", NULL, "Haptics Rate" },
+
+	{ "Noise Generator", NULL, "Noise Rate" },
+	{ "Tone Generator 1", NULL, "Tone Rate" },
+	{ "Tone Generator 2", NULL, "Tone Rate" },
+
+	{ "Mic Mute Mixer", NULL, "Mic Mute Rate" },
+
+	{ "ASRC1L", NULL, "ASRC Async Rate" },
+	{ "ASRC1R", NULL, "ASRC Async Rate" },
+	{ "ASRC2L", NULL, "ASRC Rate" },
+	{ "ASRC2R", NULL, "ASRC Rate" },
+
+	{ "ISRC1INT1", NULL, "ISRC1 FSH" },
+	{ "ISRC1INT2", NULL, "ISRC1 FSH" },
+	{ "ISRC1INT3", NULL, "ISRC1 FSH" },
+	{ "ISRC1INT4", NULL, "ISRC1 FSH" },
+
+	{ "ISRC1DEC1", NULL, "ISRC1 FSL" },
+	{ "ISRC1DEC2", NULL, "ISRC1 FSL" },
+	{ "ISRC1DEC3", NULL, "ISRC1 FSL" },
+	{ "ISRC1DEC4", NULL, "ISRC1 FSL" },
+
+	{ "ISRC2INT1", NULL, "ISRC2 FSH" },
+	{ "ISRC2INT2", NULL, "ISRC2 FSH" },
+	{ "ISRC2INT3", NULL, "ISRC2 FSH" },
+	{ "ISRC2INT4", NULL, "ISRC2 FSH" },
+
+	{ "ISRC2DEC1", NULL, "ISRC2 FSL" },
+	{ "ISRC2DEC2", NULL, "ISRC2 FSL" },
+	{ "ISRC2DEC3", NULL, "ISRC2 FSL" },
+	{ "ISRC2DEC4", NULL, "ISRC2 FSL" },
+
+	{ "ISRC3INT1", NULL, "ISRC3 FSH" },
+	{ "ISRC3INT2", NULL, "ISRC3 FSH" },
+	{ "ISRC3INT3", NULL, "ISRC3 FSH" },
+	{ "ISRC3INT4", NULL, "ISRC3 FSH" },
+
+	{ "ISRC3DEC1", NULL, "ISRC3 FSL" },
+	{ "ISRC3DEC2", NULL, "ISRC3 FSL" },
+	{ "ISRC3DEC3", NULL, "ISRC3 FSL" },
+	{ "ISRC3DEC4", NULL, "ISRC3 FSL" },
+
+	{ "SLIMRX1", NULL, "SLIMRX1 Rate" },
+	{ "SLIMRX2", NULL, "SLIMRX2 Rate" },
+	{ "SLIMRX3", NULL, "SLIMRX3 Rate" },
+	{ "SLIMRX4", NULL, "SLIMRX4 Rate" },
+	{ "SLIMRX5", NULL, "SLIMRX5 Rate" },
+	{ "SLIMRX6", NULL, "SLIMRX6 Rate" },
+	{ "SLIMRX7", NULL, "SLIMRX7 Rate" },
+	{ "SLIMRX8", NULL, "SLIMRX8 Rate" },
+
+	{ "Slim1 Playback", NULL, "SLIMRX1 Rate" },
+	{ "Slim1 Capture", NULL, "SLIMTX1 Rate" },
+
+	{ "Slim2 Playback", NULL, "SLIMRX5 Rate" },
+	{ "Slim2 Capture", NULL, "SLIMTX5 Rate" },
+
+	{ "Slim3 Playback", NULL, "SLIMRX7 Rate" },
+	{ "Slim3 Capture", NULL, "SLIMTX7 Rate" },
+
 	{ "AIF2 Capture", NULL, "DBVDD2" },
 	{ "AIF2 Playback", NULL, "DBVDD2" },
 
@@ -1878,108 +2073,108 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
 	{ "IN4L PGA", NULL, "IN4L" },
 	{ "IN4R PGA", NULL, "IN4R" },
 
-	ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
-	ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
-	ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
-	ARIZONA_MIXER_ROUTES("OUT2R", "HPOUT2R"),
-	ARIZONA_MIXER_ROUTES("OUT3L", "HPOUT3L"),
-	ARIZONA_MIXER_ROUTES("OUT3R", "HPOUT3R"),
-
-	ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
-	ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"),
-	ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
-	ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
-	ARIZONA_MIXER_ROUTES("OUT6L", "SPKDAT2L"),
-	ARIZONA_MIXER_ROUTES("OUT6R", "SPKDAT2R"),
-
-	ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
-	ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
-
-	ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
-	ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
-	ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
-	ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
-	ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
-	ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
-	ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
-	ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
-
-	ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
-	ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
-	ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
-	ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
-	ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"),
-	ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"),
-
-	ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
-	ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
-
-	ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
-	ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
-	ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
-	ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
-	ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
-	ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
-	ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
-	ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
-
-	ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
-	ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
-	ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
-	ARIZONA_MIXER_ROUTES("EQ4", "EQ4"),
-
-	ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"),
-	ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"),
-	ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"),
-	ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"),
-
-	ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
-	ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
-	ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
-	ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
-
-	ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"),
-	ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
-
-	ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
-	ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
-	ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
-	ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
-
-	ARIZONA_DSP_ROUTES("DSP1"),
-	ARIZONA_DSP_ROUTES("DSP2"),
-	ARIZONA_DSP_ROUTES("DSP3"),
-	ARIZONA_DSP_ROUTES("DSP4"),
-
-	ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
-	ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
-	ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
-	ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
-
-	ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
-	ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
-	ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
-	ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
-
-	ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
-	ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
-	ARIZONA_MUX_ROUTES("ISRC2INT3", "ISRC2INT3"),
-	ARIZONA_MUX_ROUTES("ISRC2INT4", "ISRC2INT4"),
-
-	ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
-	ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
-	ARIZONA_MUX_ROUTES("ISRC2DEC3", "ISRC2DEC3"),
-	ARIZONA_MUX_ROUTES("ISRC2DEC4", "ISRC2DEC4"),
-
-	ARIZONA_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"),
-	ARIZONA_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"),
-	ARIZONA_MUX_ROUTES("ISRC3INT3", "ISRC3INT3"),
-	ARIZONA_MUX_ROUTES("ISRC3INT4", "ISRC3INT4"),
-
-	ARIZONA_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"),
-	ARIZONA_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"),
-	ARIZONA_MUX_ROUTES("ISRC3DEC3", "ISRC3DEC3"),
-	ARIZONA_MUX_ROUTES("ISRC3DEC4", "ISRC3DEC4"),
+	ARIZONA_MIXER_ROUTES_R("OUT1L", "HPOUT1L", "Out Rate"),
+	ARIZONA_MIXER_ROUTES_R("OUT1R", "HPOUT1R", "Out Rate"),
+	ARIZONA_MIXER_ROUTES_R("OUT2L", "HPOUT2L", "Out Rate"),
+	ARIZONA_MIXER_ROUTES_R("OUT2R", "HPOUT2R", "Out Rate"),
+	ARIZONA_MIXER_ROUTES_R("OUT3L", "HPOUT3L", "Out Rate"),
+	ARIZONA_MIXER_ROUTES_R("OUT3R", "HPOUT3R", "Out Rate"),
+
+	ARIZONA_MIXER_ROUTES_R("OUT4L", "SPKOUTL", "Out Rate"),
+	ARIZONA_MIXER_ROUTES_R("OUT4R", "SPKOUTR", "Out Rate"),
+	ARIZONA_MIXER_ROUTES_R("OUT5L", "SPKDAT1L", "Out Rate"),
+	ARIZONA_MIXER_ROUTES_R("OUT5R", "SPKDAT1R", "Out Rate"),
+	ARIZONA_MIXER_ROUTES_R("OUT6L", "SPKDAT2L", "Out Rate"),
+	ARIZONA_MIXER_ROUTES_R("OUT6R", "SPKDAT2R", "Out Rate"),
+
+	ARIZONA_MIXER_ROUTES_R("PWM1 Driver", "PWM1", "PWM Rate"),
+	ARIZONA_MIXER_ROUTES_R("PWM2 Driver", "PWM2", "PWM Rate"),
+
+	ARIZONA_MIXER_ROUTES_R("AIF1TX1", "AIF1TX1", "AIF1 Rate"),
+	ARIZONA_MIXER_ROUTES_R("AIF1TX2", "AIF1TX2", "AIF1 Rate"),
+	ARIZONA_MIXER_ROUTES_R("AIF1TX3", "AIF1TX3", "AIF1 Rate"),
+	ARIZONA_MIXER_ROUTES_R("AIF1TX4", "AIF1TX4", "AIF1 Rate"),
+	ARIZONA_MIXER_ROUTES_R("AIF1TX5", "AIF1TX5", "AIF1 Rate"),
+	ARIZONA_MIXER_ROUTES_R("AIF1TX6", "AIF1TX6", "AIF1 Rate"),
+	ARIZONA_MIXER_ROUTES_R("AIF1TX7", "AIF1TX7", "AIF1 Rate"),
+	ARIZONA_MIXER_ROUTES_R("AIF1TX8", "AIF1TX8", "AIF1 Rate"),
+
+	ARIZONA_MIXER_ROUTES_R("AIF2TX1", "AIF2TX1", "AIF2 Rate"),
+	ARIZONA_MIXER_ROUTES_R("AIF2TX2", "AIF2TX2", "AIF2 Rate"),
+	ARIZONA_MIXER_ROUTES_R("AIF2TX3", "AIF2TX3", "AIF2 Rate"),
+	ARIZONA_MIXER_ROUTES_R("AIF2TX4", "AIF2TX4", "AIF2 Rate"),
+	ARIZONA_MIXER_ROUTES_R("AIF2TX5", "AIF2TX5", "AIF2 Rate"),
+	ARIZONA_MIXER_ROUTES_R("AIF2TX6", "AIF2TX6", "AIF2 Rate"),
+
+	ARIZONA_MIXER_ROUTES_R("AIF3TX1", "AIF3TX1", "AIF3 Rate"),
+	ARIZONA_MIXER_ROUTES_R("AIF3TX2", "AIF3TX2", "AIF3 Rate"),
+
+	ARIZONA_MIXER_ROUTES_R("SLIMTX1", "SLIMTX1", "SLIMTX1 Rate"),
+	ARIZONA_MIXER_ROUTES_R("SLIMTX2", "SLIMTX2", "SLIMTX2 Rate"),
+	ARIZONA_MIXER_ROUTES_R("SLIMTX3", "SLIMTX3", "SLIMTX3 Rate"),
+	ARIZONA_MIXER_ROUTES_R("SLIMTX4", "SLIMTX4", "SLIMTX4 Rate"),
+	ARIZONA_MIXER_ROUTES_R("SLIMTX5", "SLIMTX5", "SLIMTX5 Rate"),
+	ARIZONA_MIXER_ROUTES_R("SLIMTX6", "SLIMTX6", "SLIMTX6 Rate"),
+	ARIZONA_MIXER_ROUTES_R("SLIMTX7", "SLIMTX7", "SLIMTX7 Rate"),
+	ARIZONA_MIXER_ROUTES_R("SLIMTX8", "SLIMTX8", "SLIMTX8 Rate"),
+
+	ARIZONA_MIXER_ROUTES_R("EQ1", "EQ1", "FX Rate"),
+	ARIZONA_MIXER_ROUTES_R("EQ2", "EQ2", "FX Rate"),
+	ARIZONA_MIXER_ROUTES_R("EQ3", "EQ3", "FX Rate"),
+	ARIZONA_MIXER_ROUTES_R("EQ4", "EQ4", "FX Rate"),
+
+	ARIZONA_MIXER_ROUTES_R("DRC1L", "DRC1L", "FX Rate"),
+	ARIZONA_MIXER_ROUTES_R("DRC1R", "DRC1R", "FX Rate"),
+	ARIZONA_MIXER_ROUTES_R("DRC2L", "DRC2L", "FX Rate"),
+	ARIZONA_MIXER_ROUTES_R("DRC2R", "DRC2R", "FX Rate"),
+
+	ARIZONA_MIXER_ROUTES_R("LHPF1", "LHPF1", "FX Rate"),
+	ARIZONA_MIXER_ROUTES_R("LHPF2", "LHPF2", "FX Rate"),
+	ARIZONA_MIXER_ROUTES_R("LHPF3", "LHPF3", "FX Rate"),
+	ARIZONA_MIXER_ROUTES_R("LHPF4", "LHPF4", "FX Rate"),
+
+	ARIZONA_MIXER_ROUTES_R("Mic Mute Mixer", "Noise", "Mic Mute Rate"),
+	ARIZONA_MIXER_ROUTES_R("Mic Mute Mixer", "Mic", "Mic Mute Rate"),
+
+	ARIZONA_MUX_ROUTES_R("ASRC1L", "ASRC1L", "ASRC Rate"),
+	ARIZONA_MUX_ROUTES_R("ASRC1R", "ASRC1R", "ASRC Rate"),
+	ARIZONA_MUX_ROUTES_R("ASRC2L", "ASRC2L", "ASRC Async Rate"),
+	ARIZONA_MUX_ROUTES_R("ASRC2R", "ASRC2R", "ASRC Async Rate"),
+
+	ARIZONA_DSP_ROUTES_R("DSP1", "DSP1 Rate"),
+	ARIZONA_DSP_ROUTES_R("DSP2", "DSP2 Rate"),
+	ARIZONA_DSP_ROUTES_R("DSP3", "DSP3 Rate"),
+	ARIZONA_DSP_ROUTES_R("DSP4", "DSP4 Rate"),
+
+	ARIZONA_MUX_ROUTES_R("ISRC1INT1", "ISRC1INT1", "ISRC1 FSL"),
+	ARIZONA_MUX_ROUTES_R("ISRC1INT2", "ISRC1INT2", "ISRC1 FSL"),
+	ARIZONA_MUX_ROUTES_R("ISRC1INT3", "ISRC1INT3", "ISRC1 FSL"),
+	ARIZONA_MUX_ROUTES_R("ISRC1INT4", "ISRC1INT4", "ISRC1 FSL"),
+
+	ARIZONA_MUX_ROUTES_R("ISRC1DEC1", "ISRC1DEC1", "ISRC1 FSH"),
+	ARIZONA_MUX_ROUTES_R("ISRC1DEC2", "ISRC1DEC2", "ISRC1 FSH"),
+	ARIZONA_MUX_ROUTES_R("ISRC1DEC3", "ISRC1DEC3", "ISRC1 FSH"),
+	ARIZONA_MUX_ROUTES_R("ISRC1DEC4", "ISRC1DEC4", "ISRC1 FSH"),
+
+	ARIZONA_MUX_ROUTES_R("ISRC2INT1", "ISRC2INT1", "ISRC2 FSL"),
+	ARIZONA_MUX_ROUTES_R("ISRC2INT2", "ISRC2INT2", "ISRC2 FSL"),
+	ARIZONA_MUX_ROUTES_R("ISRC2INT3", "ISRC2INT3", "ISRC2 FSL"),
+	ARIZONA_MUX_ROUTES_R("ISRC2INT4", "ISRC2INT4", "ISRC2 FSL"),
+
+	ARIZONA_MUX_ROUTES_R("ISRC2DEC1", "ISRC2DEC1", "ISRC2 FSH"),
+	ARIZONA_MUX_ROUTES_R("ISRC2DEC2", "ISRC2DEC2", "ISRC2 FSH"),
+	ARIZONA_MUX_ROUTES_R("ISRC2DEC3", "ISRC2DEC3", "ISRC2 FSH"),
+	ARIZONA_MUX_ROUTES_R("ISRC2DEC4", "ISRC2DEC4", "ISRC2 FSH"),
+
+	ARIZONA_MUX_ROUTES_R("ISRC3INT1", "ISRC3INT1", "ISRC3 FSL"),
+	ARIZONA_MUX_ROUTES_R("ISRC3INT2", "ISRC3INT2", "ISRC3 FSL"),
+	ARIZONA_MUX_ROUTES_R("ISRC3INT3", "ISRC3INT3", "ISRC3 FSL"),
+	ARIZONA_MUX_ROUTES_R("ISRC3INT4", "ISRC3INT4", "ISRC3 FSL"),
+
+	ARIZONA_MUX_ROUTES_R("ISRC3DEC1", "ISRC3DEC1", "ISRC3 FSH"),
+	ARIZONA_MUX_ROUTES_R("ISRC3DEC2", "ISRC3DEC2", "ISRC3 FSH"),
+	ARIZONA_MUX_ROUTES_R("ISRC3DEC3", "ISRC3DEC3", "ISRC3 FSH"),
+	ARIZONA_MUX_ROUTES_R("ISRC3DEC4", "ISRC3DEC4", "ISRC3 FSH"),
 
 	{ "AEC Loopback", "HPOUT1L", "OUT1L" },
 	{ "AEC Loopback", "HPOUT1R", "OUT1R" },
@@ -2319,6 +2514,19 @@ static int wm5110_component_probe(struct snd_soc_component *component)
 
 	snd_soc_component_disable_pin(component, "HAPTICS");
 
+	snd_soc_dapm_connect_domains(dapm, "SLIMRX1 Rate", "SLIMRX2 Rate");
+	snd_soc_dapm_connect_domains(dapm, "SLIMRX1 Rate", "SLIMRX3 Rate");
+	snd_soc_dapm_connect_domains(dapm, "SLIMRX1 Rate", "SLIMRX4 Rate");
+	snd_soc_dapm_connect_domains(dapm, "SLIMTX1 Rate", "SLIMTX2 Rate");
+	snd_soc_dapm_connect_domains(dapm, "SLIMTX1 Rate", "SLIMTX3 Rate");
+	snd_soc_dapm_connect_domains(dapm, "SLIMTX1 Rate", "SLIMTX4 Rate");
+
+	snd_soc_dapm_connect_domains(dapm, "SLIMRX5 Rate", "SLIMRX6 Rate");
+	snd_soc_dapm_connect_domains(dapm, "SLIMTX5 Rate", "SLIMTX6 Rate");
+
+	snd_soc_dapm_connect_domains(dapm, "SLIMRX7 Rate", "SLIMRX8 Rate");
+	snd_soc_dapm_connect_domains(dapm, "SLIMTX7 Rate", "SLIMTX8 Rate");
+
 	return 0;
 
 err_adsp2_codec_probe:
@@ -2379,6 +2587,8 @@ static const struct snd_soc_component_driver soc_component_dev_wm5110 = {
 	.num_dapm_widgets	= ARRAY_SIZE(wm5110_dapm_widgets),
 	.dapm_routes		= wm5110_dapm_routes,
 	.num_dapm_routes	= ARRAY_SIZE(wm5110_dapm_routes),
+	.domains		= arizona_rate_domains,
+	.num_domains		= ARRAY_SIZE(arizona_rate_domains),
 	.use_pmdown_time	= 1,
 	.endianness		= 1,
 	.non_legacy_dai_naming	= 1,
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Applied "ASoC: dapm: Add support for hw_free on CODEC to CODEC links" to the asoc tree
  2018-10-11 16:28 ` [RFC PATCH 1/4] ASoC: dapm: Add support for hw_free on CODEC to CODEC links Charles Keepax
@ 2018-10-19 12:26   ` Mark Brown
  2018-10-19 12:34   ` Mark Brown
  1 sibling, 0 replies; 9+ messages in thread
From: Mark Brown @ 2018-10-19 12:26 UTC (permalink / raw)
  To: Charles Keepax; +Cc: patches, alsa-devel, broonie, lgirdwood

The patch

   ASoC: dapm: Add support for hw_free on CODEC to CODEC links

has been applied to the asoc tree at

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 3c01b0e129e9486c8004e43eba3a70de7393f645 Mon Sep 17 00:00:00 2001
From: Charles Keepax <ckeepax@opensource.cirrus.com>
Date: Thu, 11 Oct 2018 17:28:28 +0100
Subject: [PATCH] ASoC: dapm: Add support for hw_free on CODEC to CODEC links

Currently, on power down for a CODEC to CODEC DAI link we only call
digital_mute and shutdown. Provide a little more flexibility for drivers
by adding a call to hw_free as well.

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 sound/soc/soc-dapm.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 8c5b065c8880..a5178845065b 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -3737,25 +3737,30 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
 			ret = 0;
 		}
 
+		substream.stream = SNDRV_PCM_STREAM_CAPTURE;
 		snd_soc_dapm_widget_for_each_source_path(w, path) {
 			source = path->source->priv;
 
+			if (source->driver->ops->hw_free)
+				source->driver->ops->hw_free(&substream,
+							     source);
+
 			source->active--;
-			if (source->driver->ops->shutdown) {
-				substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+			if (source->driver->ops->shutdown)
 				source->driver->ops->shutdown(&substream,
 							      source);
-			}
 		}
 
+		substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
 		snd_soc_dapm_widget_for_each_sink_path(w, path) {
 			sink = path->sink->priv;
 
+			if (sink->driver->ops->hw_free)
+				sink->driver->ops->hw_free(&substream, sink);
+
 			sink->active--;
-			if (sink->driver->ops->shutdown) {
-				substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+			if (sink->driver->ops->shutdown)
 				sink->driver->ops->shutdown(&substream, sink);
-			}
 		}
 		break;
 
-- 
2.19.0.rc2

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Applied "ASoC: dapm: Add support for hw_free on CODEC to CODEC links" to the asoc tree
  2018-10-11 16:28 ` [RFC PATCH 1/4] ASoC: dapm: Add support for hw_free on CODEC to CODEC links Charles Keepax
  2018-10-19 12:26   ` Applied "ASoC: dapm: Add support for hw_free on CODEC to CODEC links" to the asoc tree Mark Brown
@ 2018-10-19 12:34   ` Mark Brown
  1 sibling, 0 replies; 9+ messages in thread
From: Mark Brown @ 2018-10-19 12:34 UTC (permalink / raw)
  To: Charles Keepax; +Cc: patches, alsa-devel, broonie, lgirdwood

The patch

   ASoC: dapm: Add support for hw_free on CODEC to CODEC links

has been applied to the asoc tree at

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 3c01b0e129e9486c8004e43eba3a70de7393f645 Mon Sep 17 00:00:00 2001
From: Charles Keepax <ckeepax@opensource.cirrus.com>
Date: Thu, 11 Oct 2018 17:28:28 +0100
Subject: [PATCH] ASoC: dapm: Add support for hw_free on CODEC to CODEC links

Currently, on power down for a CODEC to CODEC DAI link we only call
digital_mute and shutdown. Provide a little more flexibility for drivers
by adding a call to hw_free as well.

Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 sound/soc/soc-dapm.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 8c5b065c8880..a5178845065b 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -3737,25 +3737,30 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
 			ret = 0;
 		}
 
+		substream.stream = SNDRV_PCM_STREAM_CAPTURE;
 		snd_soc_dapm_widget_for_each_source_path(w, path) {
 			source = path->source->priv;
 
+			if (source->driver->ops->hw_free)
+				source->driver->ops->hw_free(&substream,
+							     source);
+
 			source->active--;
-			if (source->driver->ops->shutdown) {
-				substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+			if (source->driver->ops->shutdown)
 				source->driver->ops->shutdown(&substream,
 							      source);
-			}
 		}
 
+		substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
 		snd_soc_dapm_widget_for_each_sink_path(w, path) {
 			sink = path->sink->priv;
 
+			if (sink->driver->ops->hw_free)
+				sink->driver->ops->hw_free(&substream, sink);
+
 			sink->active--;
-			if (sink->driver->ops->shutdown) {
-				substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+			if (sink->driver->ops->shutdown)
 				sink->driver->ops->shutdown(&substream, sink);
-			}
 		}
 		break;
 
-- 
2.19.0.rc2

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 0/4] Initial prototype of DAPM sample rate domains
  2018-10-11 16:28 [RFC PATCH 0/4] Initial prototype of DAPM sample rate domains Charles Keepax
                   ` (3 preceding siblings ...)
  2018-10-11 16:28 ` [RFC PATCH 4/4] ASoC: arizona: Add " Charles Keepax
@ 2018-10-22 14:27 ` Pierre-Louis Bossart
  2018-10-23 14:36   ` Charles Keepax
  4 siblings, 1 reply; 9+ messages in thread
From: Pierre-Louis Bossart @ 2018-10-22 14:27 UTC (permalink / raw)
  To: Charles Keepax, broonie; +Cc: patches, alsa-devel, lgirdwood


On 10/11/18 11:28 AM, Charles Keepax wrote:
> Here is a first pass at adding dapm sample rate domain
> support. Things are still pretty rough in quite a few places and
> I expect some major refactoring before they are ready to merge,
> but things are far enough along for people to look at the approach
> I am taking to the problem. And should provide enough for discussion
> at the mini-conference, although hopefully I will get some more done
> next week as well.
Interesting, too bad I couldn't be in Edinburgh this year.
>
> The basic high level summary is two new concepts are added to
> ASoC, one being a sample rate domain and the other being a domain
> group. The domain groups represent widgets that are limited by
> hardware restrictions to always run at the same sample rate as each
> other. Sample rate domains represent a slot on a device for a sample
> rate. Both of these are currently limited to be within the scope of
> a single component.  The core will track which groups are connected
> together, then as a group is powered up it will be allocated a domain
> based on the other groups it is connected to. Domain groups are
> implemented as a new type of DAPM widget and the domains themselves
> are a new thing which devices add through their component drivers.
>
> As we have previously discussed my intention would be that we also
> add bridges between domains although I haven't started work on that
> yet. The current code is limited to within a single component and
> I think that the bridges will allow better support for propagating
> rates between components as well as obviously for SRCs. Currently
> the SRCs on wm5110 are just being handled as points where the
> domain groups don't connect to another peer.  This allows them to
> function but doesn't accurately convey things like the limitations
> which input rates support which output rates on the ISRC.

My feedback would be: why limit the domains to sample rate definition? 
we can also have additional criteria such as number of channels, channel 
maps and bit depth - maybe also audio/non-audio for compressed stuff. 
It'd be interesting to start with a non-scalar descriptor from day1. To 
some extent we'd also want something similar to .info definitions, with 
domains defined with masks to allow for flexibility in the conversions 
and easier bridge implementations. e.g. it'd be interesting to have a 
domain definition supporting more than one sampling frequency (8, 16, 
48kHz) with the bridge doing the relevant conversion to adapt to the 
connected domain (fixed 48kHz DAI for example)

Thanks for starting the discussion!

-Pierre

>
> Thanks,
> Charles
>
> Charles Keepax (4):
>    ASoC: dapm: Add support for hw_free on CODEC to CODEC links
>    ASoC: dapm: Add support for a rate domain widget
>    ASoC: domain: Add sample rate domain support
>    ASoC: arizona: Add rate domain support
>
>   include/sound/soc-dapm.h   |  14 ++
>   include/sound/soc-domain.h |  98 +++++++++++
>   include/sound/soc.h        |   8 +
>   sound/soc/Makefile         |   2 +-
>   sound/soc/codecs/arizona.c | 255 ++++++++++++++++++++++------
>   sound/soc/codecs/arizona.h |  76 ++++++++-
>   sound/soc/codecs/wm5110.c  | 414 ++++++++++++++++++++++++++++++++++-----------
>   sound/soc/soc-core.c       |   8 +
>   sound/soc/soc-dapm.c       |  57 ++++++-
>   sound/soc/soc-domain.c     | 412 ++++++++++++++++++++++++++++++++++++++++++++
>   10 files changed, 1183 insertions(+), 161 deletions(-)
>   create mode 100644 include/sound/soc-domain.h
>   create mode 100644 sound/soc/soc-domain.c
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH 0/4] Initial prototype of DAPM sample rate domains
  2018-10-22 14:27 ` [RFC PATCH 0/4] Initial prototype of DAPM sample rate domains Pierre-Louis Bossart
@ 2018-10-23 14:36   ` Charles Keepax
  0 siblings, 0 replies; 9+ messages in thread
From: Charles Keepax @ 2018-10-23 14:36 UTC (permalink / raw)
  To: Pierre-Louis Bossart; +Cc: patches, alsa-devel, broonie, lgirdwood

On Mon, Oct 22, 2018 at 09:27:13AM -0500, Pierre-Louis Bossart wrote:
> 
> On 10/11/18 11:28 AM, Charles Keepax wrote:
> My feedback would be: why limit the domains to sample rate
> definition? we can also have additional criteria such as number of
> channels, channel maps and bit depth - maybe also audio/non-audio
> for compressed stuff. It'd be interesting to start with a non-scalar
> descriptor from day1. To some extent we'd also want something
> similar to .info definitions, with domains defined with masks to
> allow for flexibility in the conversions and easier bridge
> implementations. e.g. it'd be interesting to have a domain
> definition supporting more than one sampling frequency (8, 16,
> 48kHz) with the bridge doing the relevant conversion to adapt to the
> connected domain (fixed 48kHz DAI for example)
> 
> Thanks for starting the discussion!

I don't think there really is anything about the way I am
approaching the code that will limit this to just sample rates,
it's more that is the part I need at the moment so is what
I am implementing. I have basically a "domain" structure,
that gets assigned as the groups power up, this is in no way
specific to rates. We can just extend that struct to have other
properties and add new ops on the domains as people discover
additional features they require. Currently at that point in
the code you will see a TODO which says to consider using a
hw_params struct instead of just an int for the rate. I think
based on your comments and others at the conference I will
definitely be switching to that in the next spin. Although
implementing the additional callbacks etc. is not something I
can find sensible use-cases for on our devices, so someone else
might need to pick up that part.

Thanks,
Charles

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2018-10-23 14:36 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-10-11 16:28 [RFC PATCH 0/4] Initial prototype of DAPM sample rate domains Charles Keepax
2018-10-11 16:28 ` [RFC PATCH 1/4] ASoC: dapm: Add support for hw_free on CODEC to CODEC links Charles Keepax
2018-10-19 12:26   ` Applied "ASoC: dapm: Add support for hw_free on CODEC to CODEC links" to the asoc tree Mark Brown
2018-10-19 12:34   ` Mark Brown
2018-10-11 16:28 ` [RFC PATCH 2/4] ASoC: dapm: Add support for a rate domain widget Charles Keepax
2018-10-11 16:28 ` [RFC PATCH 3/4] ASoC: domain: Add sample rate domain support Charles Keepax
2018-10-11 16:28 ` [RFC PATCH 4/4] ASoC: arizona: Add " Charles Keepax
2018-10-22 14:27 ` [RFC PATCH 0/4] Initial prototype of DAPM sample rate domains Pierre-Louis Bossart
2018-10-23 14:36   ` Charles Keepax

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.