瀏覽代碼

Merge remote-tracking branches 'asoc/topic/max98095', 'asoc/topic/omap', 'asoc/topic/pxa', 'asoc/topic/qcom' and 'asoc/topic/rcar' into asoc-next

Mark Brown 10 年之前
父節點
當前提交
a178831a63

+ 12 - 1
Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt

@@ -4,12 +4,21 @@ This node models the Qualcomm Technologies Low-Power Audio SubSystem (LPASS).
 
 
 Required properties:
 Required properties:
 
 
-- compatible		: "qcom,lpass-cpu"
+- compatible		: "qcom,lpass-cpu" or "qcom,apq8016-lpass-cpu"
 - clocks		: Must contain an entry for each entry in clock-names.
 - clocks		: Must contain an entry for each entry in clock-names.
 - clock-names		: A list which must include the following entries:
 - clock-names		: A list which must include the following entries:
 				* "ahbix-clk"
 				* "ahbix-clk"
 				* "mi2s-osr-clk"
 				* "mi2s-osr-clk"
 				* "mi2s-bit-clk"
 				* "mi2s-bit-clk"
+			: required clocks for "qcom,lpass-cpu-apq8016"
+				* "ahbix-clk"
+				* "mi2s-bit-clk0"
+				* "mi2s-bit-clk1"
+				* "mi2s-bit-clk2"
+				* "mi2s-bit-clk3"
+				* "pcnoc-mport-clk"
+				* "pcnoc-sway-clk"
+
 - interrupts		: Must contain an entry for each entry in
 - interrupts		: Must contain an entry for each entry in
 			  interrupt-names.
 			  interrupt-names.
 - interrupt-names	: A list which must include the following entries:
 - interrupt-names	: A list which must include the following entries:
@@ -22,6 +31,8 @@ Required properties:
 - reg-names		: A list which must include the following entries:
 - reg-names		: A list which must include the following entries:
 				* "lpass-lpaif"
 				* "lpass-lpaif"
 
 
+
+
 Optional properties:
 Optional properties:
 
 
 - qcom,adsp		: Phandle for the audio DSP node
 - qcom,adsp		: Phandle for the audio DSP node

+ 1 - 1
Documentation/devicetree/bindings/sound/renesas,rsnd.txt

@@ -48,7 +48,7 @@ DAI subnode properties:
 
 
 Example:
 Example:
 
 
-rcar_sound: rcar_sound@ec500000 {
+rcar_sound: sound@ec500000 {
 	#sound-dai-cells = <1>;
 	#sound-dai-cells = <1>;
 	compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2";
 	compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2";
 	reg =	<0 0xec500000 0 0x1000>, /* SCU */
 	reg =	<0 0xec500000 0 0x1000>, /* SCU */

+ 21 - 16
drivers/dma/sh/rcar-dmac.c

@@ -465,6 +465,7 @@ static dma_cookie_t rcar_dmac_tx_submit(struct dma_async_tx_descriptor *tx)
 static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
 static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
 {
 {
 	struct rcar_dmac_desc_page *page;
 	struct rcar_dmac_desc_page *page;
+	unsigned long flags;
 	LIST_HEAD(list);
 	LIST_HEAD(list);
 	unsigned int i;
 	unsigned int i;
 
 
@@ -482,10 +483,10 @@ static int rcar_dmac_desc_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
 		list_add_tail(&desc->node, &list);
 		list_add_tail(&desc->node, &list);
 	}
 	}
 
 
-	spin_lock_irq(&chan->lock);
+	spin_lock_irqsave(&chan->lock, flags);
 	list_splice_tail(&list, &chan->desc.free);
 	list_splice_tail(&list, &chan->desc.free);
 	list_add_tail(&page->node, &chan->desc.pages);
 	list_add_tail(&page->node, &chan->desc.pages);
-	spin_unlock_irq(&chan->lock);
+	spin_unlock_irqrestore(&chan->lock, flags);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -516,6 +517,7 @@ static void rcar_dmac_desc_put(struct rcar_dmac_chan *chan,
 static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
 static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
 {
 {
 	struct rcar_dmac_desc *desc, *_desc;
 	struct rcar_dmac_desc *desc, *_desc;
+	unsigned long flags;
 	LIST_HEAD(list);
 	LIST_HEAD(list);
 
 
 	/*
 	/*
@@ -524,9 +526,9 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
 	 * list_for_each_entry_safe, isn't safe if we release the channel lock
 	 * list_for_each_entry_safe, isn't safe if we release the channel lock
 	 * around the rcar_dmac_desc_put() call.
 	 * around the rcar_dmac_desc_put() call.
 	 */
 	 */
-	spin_lock_irq(&chan->lock);
+	spin_lock_irqsave(&chan->lock, flags);
 	list_splice_init(&chan->desc.wait, &list);
 	list_splice_init(&chan->desc.wait, &list);
-	spin_unlock_irq(&chan->lock);
+	spin_unlock_irqrestore(&chan->lock, flags);
 
 
 	list_for_each_entry_safe(desc, _desc, &list, node) {
 	list_for_each_entry_safe(desc, _desc, &list, node) {
 		if (async_tx_test_ack(&desc->async_tx)) {
 		if (async_tx_test_ack(&desc->async_tx)) {
@@ -539,9 +541,9 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
 		return;
 		return;
 
 
 	/* Put the remaining descriptors back in the wait list. */
 	/* Put the remaining descriptors back in the wait list. */
-	spin_lock_irq(&chan->lock);
+	spin_lock_irqsave(&chan->lock, flags);
 	list_splice(&list, &chan->desc.wait);
 	list_splice(&list, &chan->desc.wait);
-	spin_unlock_irq(&chan->lock);
+	spin_unlock_irqrestore(&chan->lock, flags);
 }
 }
 
 
 /*
 /*
@@ -556,12 +558,13 @@ static void rcar_dmac_desc_recycle_acked(struct rcar_dmac_chan *chan)
 static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
 static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
 {
 {
 	struct rcar_dmac_desc *desc;
 	struct rcar_dmac_desc *desc;
+	unsigned long flags;
 	int ret;
 	int ret;
 
 
 	/* Recycle acked descriptors before attempting allocation. */
 	/* Recycle acked descriptors before attempting allocation. */
 	rcar_dmac_desc_recycle_acked(chan);
 	rcar_dmac_desc_recycle_acked(chan);
 
 
-	spin_lock_irq(&chan->lock);
+	spin_lock_irqsave(&chan->lock, flags);
 
 
 	while (list_empty(&chan->desc.free)) {
 	while (list_empty(&chan->desc.free)) {
 		/*
 		/*
@@ -570,17 +573,17 @@ static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
 		 * allocated descriptors. If the allocation fails return an
 		 * allocated descriptors. If the allocation fails return an
 		 * error.
 		 * error.
 		 */
 		 */
-		spin_unlock_irq(&chan->lock);
+		spin_unlock_irqrestore(&chan->lock, flags);
 		ret = rcar_dmac_desc_alloc(chan, GFP_NOWAIT);
 		ret = rcar_dmac_desc_alloc(chan, GFP_NOWAIT);
 		if (ret < 0)
 		if (ret < 0)
 			return NULL;
 			return NULL;
-		spin_lock_irq(&chan->lock);
+		spin_lock_irqsave(&chan->lock, flags);
 	}
 	}
 
 
 	desc = list_first_entry(&chan->desc.free, struct rcar_dmac_desc, node);
 	desc = list_first_entry(&chan->desc.free, struct rcar_dmac_desc, node);
 	list_del(&desc->node);
 	list_del(&desc->node);
 
 
-	spin_unlock_irq(&chan->lock);
+	spin_unlock_irqrestore(&chan->lock, flags);
 
 
 	return desc;
 	return desc;
 }
 }
@@ -593,6 +596,7 @@ static struct rcar_dmac_desc *rcar_dmac_desc_get(struct rcar_dmac_chan *chan)
 static int rcar_dmac_xfer_chunk_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
 static int rcar_dmac_xfer_chunk_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
 {
 {
 	struct rcar_dmac_desc_page *page;
 	struct rcar_dmac_desc_page *page;
+	unsigned long flags;
 	LIST_HEAD(list);
 	LIST_HEAD(list);
 	unsigned int i;
 	unsigned int i;
 
 
@@ -606,10 +610,10 @@ static int rcar_dmac_xfer_chunk_alloc(struct rcar_dmac_chan *chan, gfp_t gfp)
 		list_add_tail(&chunk->node, &list);
 		list_add_tail(&chunk->node, &list);
 	}
 	}
 
 
-	spin_lock_irq(&chan->lock);
+	spin_lock_irqsave(&chan->lock, flags);
 	list_splice_tail(&list, &chan->desc.chunks_free);
 	list_splice_tail(&list, &chan->desc.chunks_free);
 	list_add_tail(&page->node, &chan->desc.pages);
 	list_add_tail(&page->node, &chan->desc.pages);
-	spin_unlock_irq(&chan->lock);
+	spin_unlock_irqrestore(&chan->lock, flags);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -627,9 +631,10 @@ static struct rcar_dmac_xfer_chunk *
 rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan)
 rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan)
 {
 {
 	struct rcar_dmac_xfer_chunk *chunk;
 	struct rcar_dmac_xfer_chunk *chunk;
+	unsigned long flags;
 	int ret;
 	int ret;
 
 
-	spin_lock_irq(&chan->lock);
+	spin_lock_irqsave(&chan->lock, flags);
 
 
 	while (list_empty(&chan->desc.chunks_free)) {
 	while (list_empty(&chan->desc.chunks_free)) {
 		/*
 		/*
@@ -638,18 +643,18 @@ rcar_dmac_xfer_chunk_get(struct rcar_dmac_chan *chan)
 		 * allocated descriptors. If the allocation fails return an
 		 * allocated descriptors. If the allocation fails return an
 		 * error.
 		 * error.
 		 */
 		 */
-		spin_unlock_irq(&chan->lock);
+		spin_unlock_irqrestore(&chan->lock, flags);
 		ret = rcar_dmac_xfer_chunk_alloc(chan, GFP_NOWAIT);
 		ret = rcar_dmac_xfer_chunk_alloc(chan, GFP_NOWAIT);
 		if (ret < 0)
 		if (ret < 0)
 			return NULL;
 			return NULL;
-		spin_lock_irq(&chan->lock);
+		spin_lock_irqsave(&chan->lock, flags);
 	}
 	}
 
 
 	chunk = list_first_entry(&chan->desc.chunks_free,
 	chunk = list_first_entry(&chan->desc.chunks_free,
 				 struct rcar_dmac_xfer_chunk, node);
 				 struct rcar_dmac_xfer_chunk, node);
 	list_del(&chunk->node);
 	list_del(&chunk->node);
 
 
-	spin_unlock_irq(&chan->lock);
+	spin_unlock_irqrestore(&chan->lock, flags);
 
 
 	return chunk;
 	return chunk;
 }
 }

+ 9 - 0
include/dt-bindings/sound/apq8016-lpass.h

@@ -0,0 +1,9 @@
+#ifndef __DT_APQ8016_LPASS_H
+#define __DT_APQ8016_LPASS_H
+
+#define MI2S_PRIMARY	0
+#define MI2S_SECONDARY	1
+#define MI2S_TERTIARY	2
+#define MI2S_QUATERNARY	3
+
+#endif /* __DT_APQ8016_LPASS_H */

+ 2 - 2
sound/soc/codecs/max98095.c

@@ -2301,8 +2301,8 @@ static int max98095_probe(struct snd_soc_codec *codec)
 		/* register an audio interrupt */
 		/* register an audio interrupt */
 		ret = request_threaded_irq(client->irq, NULL,
 		ret = request_threaded_irq(client->irq, NULL,
 			max98095_report_jack,
 			max98095_report_jack,
-			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-			"max98095", codec);
+			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+			IRQF_ONESHOT, "max98095", codec);
 		if (ret) {
 		if (ret) {
 			dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
 			dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
 			goto err_access;
 			goto err_access;

+ 3 - 2
sound/soc/omap/Kconfig

@@ -100,12 +100,13 @@ config SND_OMAP_SOC_OMAP_TWL4030
 
 
 config SND_OMAP_SOC_OMAP_ABE_TWL6040
 config SND_OMAP_SOC_OMAP_ABE_TWL6040
 	tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
 	tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
-	depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST)
+	depends on TWL6040_CORE && SND_OMAP_SOC
+	depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST
 	select SND_OMAP_SOC_DMIC
 	select SND_OMAP_SOC_DMIC
 	select SND_OMAP_SOC_MCPDM
 	select SND_OMAP_SOC_MCPDM
 	select SND_SOC_TWL6040
 	select SND_SOC_TWL6040
 	select SND_SOC_DMIC
 	select SND_SOC_DMIC
-	select COMMON_CLK_PALMAS if MFD_PALMAS
+	select COMMON_CLK_PALMAS if (SOC_OMAP5 && MFD_PALMAS)
 	help
 	help
 	  Say Y if you want to add support for SoC audio on OMAP boards using
 	  Say Y if you want to add support for SoC audio on OMAP boards using
 	  ABE and twl6040 codec. This driver currently supports:
 	  ABE and twl6040 codec. This driver currently supports:

+ 1 - 2
sound/soc/omap/omap-twl4030.c

@@ -159,9 +159,8 @@ static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
 
 
 static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
 static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
 {
 {
-	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_card *card = rtd->card;
 	struct snd_soc_card *card = rtd->card;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	struct snd_soc_dapm_context *dapm = &card->dapm;
 	struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
 	struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
 	struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
 	struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
 	int ret = 0;
 	int ret = 0;

+ 1 - 24
sound/soc/pxa/brownstone.c

@@ -45,29 +45,6 @@ static const struct snd_soc_dapm_route brownstone_audio_map[] = {
 	{"MICBIAS1", NULL, "Main Mic"},
 	{"MICBIAS1", NULL, "Main Mic"},
 };
 };
 
 
-static int brownstone_wm8994_init(struct snd_soc_pcm_runtime *rtd)
-{
-	struct snd_soc_codec *codec = rtd->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-	/* set endpoints to not connected */
-	snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
-	snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
-	snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
-	snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
-	snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
-	snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
-	snd_soc_dapm_nc_pin(dapm, "IN1LN");
-	snd_soc_dapm_nc_pin(dapm, "IN1LP");
-	snd_soc_dapm_nc_pin(dapm, "IN1RP");
-	snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
-	snd_soc_dapm_nc_pin(dapm, "IN2RN");
-	snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
-	snd_soc_dapm_nc_pin(dapm, "IN2LN");
-
-	return 0;
-}
-
 static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
 static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
 				       struct snd_pcm_hw_params *params)
 				       struct snd_pcm_hw_params *params)
 {
 {
@@ -115,7 +92,6 @@ static struct snd_soc_dai_link brownstone_wm8994_dai[] = {
 	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 				SND_SOC_DAIFMT_CBS_CFS,
 				SND_SOC_DAIFMT_CBS_CFS,
 	.ops		= &brownstone_ops,
 	.ops		= &brownstone_ops,
-	.init		= brownstone_wm8994_init,
 },
 },
 };
 };
 
 
@@ -132,6 +108,7 @@ static struct snd_soc_card brownstone = {
 	.num_dapm_widgets = ARRAY_SIZE(brownstone_dapm_widgets),
 	.num_dapm_widgets = ARRAY_SIZE(brownstone_dapm_widgets),
 	.dapm_routes = brownstone_audio_map,
 	.dapm_routes = brownstone_audio_map,
 	.num_dapm_routes = ARRAY_SIZE(brownstone_audio_map),
 	.num_dapm_routes = ARRAY_SIZE(brownstone_audio_map),
+	.fully_routed = true,
 };
 };
 
 
 static int brownstone_probe(struct platform_device *pdev)
 static int brownstone_probe(struct platform_device *pdev)

+ 4 - 15
sound/soc/pxa/poodle.c

@@ -192,6 +192,7 @@ static int poodle_amp_event(struct snd_soc_dapm_widget *w,
 static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
 static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
 SND_SOC_DAPM_HP("Headphone Jack", NULL),
 SND_SOC_DAPM_HP("Headphone Jack", NULL),
 SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
 SND_SOC_DAPM_SPK("Ext Spk", poodle_amp_event),
+SND_SOC_DAPM_MIC("Microphone", NULL),
 };
 };
 
 
 /* Corgi machine connections to the codec pins */
 /* Corgi machine connections to the codec pins */
@@ -204,6 +205,8 @@ static const struct snd_soc_dapm_route poodle_audio_map[] = {
 	/* speaker connected to LOUT, ROUT */
 	/* speaker connected to LOUT, ROUT */
 	{"Ext Spk", NULL, "ROUT"},
 	{"Ext Spk", NULL, "ROUT"},
 	{"Ext Spk", NULL, "LOUT"},
 	{"Ext Spk", NULL, "LOUT"},
+
+	{"MICIN", NULL, "Microphone"},
 };
 };
 
 
 static const char *jack_function[] = {"Off", "Headphone"};
 static const char *jack_function[] = {"Off", "Headphone"};
@@ -220,20 +223,6 @@ static const struct snd_kcontrol_new wm8731_poodle_controls[] = {
 		poodle_set_spk),
 		poodle_set_spk),
 };
 };
 
 
-/*
- * Logic for a wm8731 as connected on a Sharp SL-C7x0 Device
- */
-static int poodle_wm8731_init(struct snd_soc_pcm_runtime *rtd)
-{
-	struct snd_soc_codec *codec = rtd->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-	snd_soc_dapm_nc_pin(dapm, "LLINEIN");
-	snd_soc_dapm_nc_pin(dapm, "RLINEIN");
-
-	return 0;
-}
-
 /* poodle digital audio interface glue - connects codec <--> CPU */
 /* poodle digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link poodle_dai = {
 static struct snd_soc_dai_link poodle_dai = {
 	.name = "WM8731",
 	.name = "WM8731",
@@ -242,7 +231,6 @@ static struct snd_soc_dai_link poodle_dai = {
 	.codec_dai_name = "wm8731-hifi",
 	.codec_dai_name = "wm8731-hifi",
 	.platform_name = "pxa-pcm-audio",
 	.platform_name = "pxa-pcm-audio",
 	.codec_name = "wm8731.0-001b",
 	.codec_name = "wm8731.0-001b",
-	.init = poodle_wm8731_init,
 	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 	.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
 		   SND_SOC_DAIFMT_CBS_CFS,
 		   SND_SOC_DAIFMT_CBS_CFS,
 	.ops = &poodle_ops,
 	.ops = &poodle_ops,
@@ -261,6 +249,7 @@ static struct snd_soc_card poodle = {
 	.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
 	.num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
 	.dapm_routes = poodle_audio_map,
 	.dapm_routes = poodle_audio_map,
 	.num_dapm_routes = ARRAY_SIZE(poodle_audio_map),
 	.num_dapm_routes = ARRAY_SIZE(poodle_audio_map),
+	.fully_routed = true,
 };
 };
 
 
 static int poodle_probe(struct platform_device *pdev)
 static int poodle_probe(struct platform_device *pdev)

+ 1 - 12
sound/soc/pxa/tosa.c

@@ -185,17 +185,6 @@ static const struct snd_kcontrol_new tosa_controls[] = {
 		tosa_set_spk),
 		tosa_set_spk),
 };
 };
 
 
-static int tosa_ac97_init(struct snd_soc_pcm_runtime *rtd)
-{
-	struct snd_soc_codec *codec = rtd->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-	snd_soc_dapm_nc_pin(dapm, "OUT3");
-	snd_soc_dapm_nc_pin(dapm, "MONOOUT");
-
-	return 0;
-}
-
 static struct snd_soc_dai_link tosa_dai[] = {
 static struct snd_soc_dai_link tosa_dai[] = {
 {
 {
 	.name = "AC97",
 	.name = "AC97",
@@ -204,7 +193,6 @@ static struct snd_soc_dai_link tosa_dai[] = {
 	.codec_dai_name = "wm9712-hifi",
 	.codec_dai_name = "wm9712-hifi",
 	.platform_name = "pxa-pcm-audio",
 	.platform_name = "pxa-pcm-audio",
 	.codec_name = "wm9712-codec",
 	.codec_name = "wm9712-codec",
-	.init = tosa_ac97_init,
 	.ops = &tosa_ops,
 	.ops = &tosa_ops,
 },
 },
 {
 {
@@ -230,6 +218,7 @@ static struct snd_soc_card tosa = {
 	.num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets),
 	.num_dapm_widgets = ARRAY_SIZE(tosa_dapm_widgets),
 	.dapm_routes = audio_map,
 	.dapm_routes = audio_map,
 	.num_dapm_routes = ARRAY_SIZE(audio_map),
 	.num_dapm_routes = ARRAY_SIZE(audio_map),
+	.fully_routed = true,
 };
 };
 
 
 static int tosa_probe(struct platform_device *pdev)
 static int tosa_probe(struct platform_device *pdev)

+ 1 - 8
sound/soc/pxa/z2.c

@@ -132,16 +132,8 @@ static const struct snd_soc_dapm_route z2_audio_map[] = {
  */
  */
 static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
 static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
 {
 {
-	struct snd_soc_codec *codec = rtd->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret;
 	int ret;
 
 
-	/* NC codec pins */
-	snd_soc_dapm_disable_pin(dapm, "LINPUT3");
-	snd_soc_dapm_disable_pin(dapm, "RINPUT3");
-	snd_soc_dapm_disable_pin(dapm, "OUT3");
-	snd_soc_dapm_disable_pin(dapm, "MONO1");
-
 	/* Jack detection API stuff */
 	/* Jack detection API stuff */
 	ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
 	ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
 				    &hs_jack, hs_jack_pins,
 				    &hs_jack, hs_jack_pins,
@@ -189,6 +181,7 @@ static struct snd_soc_card snd_soc_z2 = {
 	.num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
 	.num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
 	.dapm_routes = z2_audio_map,
 	.dapm_routes = z2_audio_map,
 	.num_dapm_routes = ARRAY_SIZE(z2_audio_map),
 	.num_dapm_routes = ARRAY_SIZE(z2_audio_map),
+	.fully_routed = true,
 };
 };
 
 
 static struct platform_device *z2_snd_device;
 static struct platform_device *z2_snd_device;

+ 14 - 5
sound/soc/qcom/Kconfig

@@ -6,19 +6,28 @@ config SND_SOC_QCOM
 
 
 config SND_SOC_LPASS_CPU
 config SND_SOC_LPASS_CPU
 	tristate
 	tristate
-	depends on SND_SOC_QCOM
 	select REGMAP_MMIO
 	select REGMAP_MMIO
 
 
 config SND_SOC_LPASS_PLATFORM
 config SND_SOC_LPASS_PLATFORM
 	tristate
 	tristate
-	depends on SND_SOC_QCOM
 	select REGMAP_MMIO
 	select REGMAP_MMIO
 
 
-config SND_SOC_STORM
-	tristate "ASoC I2S support for Storm boards"
-	depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST
+config SND_SOC_LPASS_IPQ806X
+	tristate
+	depends on SND_SOC_QCOM
+	select SND_SOC_LPASS_CPU
+	select SND_SOC_LPASS_PLATFORM
+
+config SND_SOC_LPASS_APQ8016
+	tristate
+	depends on SND_SOC_QCOM
 	select SND_SOC_LPASS_CPU
 	select SND_SOC_LPASS_CPU
 	select SND_SOC_LPASS_PLATFORM
 	select SND_SOC_LPASS_PLATFORM
+
+config SND_SOC_STORM
+	tristate "ASoC I2S support for Storm boards"
+	depends on SND_SOC_QCOM && (ARCH_QCOM || COMPILE_TEST)
+	select SND_SOC_LPASS_IPQ806X
 	select SND_SOC_MAX98357A
 	select SND_SOC_MAX98357A
 	help
 	help
           Say Y or M if you want add support for SoC audio on the
           Say Y or M if you want add support for SoC audio on the

+ 4 - 0
sound/soc/qcom/Makefile

@@ -1,9 +1,13 @@
 # Platform
 # Platform
 snd-soc-lpass-cpu-objs := lpass-cpu.o
 snd-soc-lpass-cpu-objs := lpass-cpu.o
 snd-soc-lpass-platform-objs := lpass-platform.o
 snd-soc-lpass-platform-objs := lpass-platform.o
+snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o
+snd-soc-lpass-apq8016-objs := lpass-apq8016.o
 
 
 obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
 obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
 obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
 obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
+obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o
+obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o
 
 
 # Machine
 # Machine
 snd-soc-storm-objs := storm.o
 snd-soc-storm-objs := storm.o

+ 242 - 0
sound/soc/qcom/lpass-apq8016.c

@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * lpass-apq8016.c -- ALSA SoC CPU DAI driver for APQ8016 LPASS
+ *
+ */
+
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include <dt-bindings/sound/apq8016-lpass.h>
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+
+static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
+	[MI2S_PRIMARY] =  {
+		.id = MI2S_PRIMARY,
+		.name = "Primary MI2S",
+		.playback = {
+			.stream_name	= "Primary Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+		.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+	},
+	[MI2S_SECONDARY] =  {
+		.id = MI2S_SECONDARY,
+		.name = "Secondary MI2S",
+		.playback = {
+			.stream_name	= "Secondary Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+		.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+	},
+	[MI2S_TERTIARY] =  {
+		.id = MI2S_TERTIARY,
+		.name = "Tertiary MI2S",
+		.capture = {
+			.stream_name	= "Tertiary Capture",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+		.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+	},
+	[MI2S_QUATERNARY] =  {
+		.id = MI2S_QUATERNARY,
+		.name = "Quatenary MI2S",
+		.playback = {
+			.stream_name	= "Quatenary Playback",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.capture = {
+			.stream_name	= "Quatenary Capture",
+			.formats	= SNDRV_PCM_FMTBIT_S16 |
+						SNDRV_PCM_FMTBIT_S24 |
+						SNDRV_PCM_FMTBIT_S32,
+			.rates		= SNDRV_PCM_RATE_8000 |
+						SNDRV_PCM_RATE_16000 |
+						SNDRV_PCM_RATE_32000 |
+						SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_96000,
+			.rate_min	= 8000,
+			.rate_max	= 96000,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
+		.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+		.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+	},
+};
+
+static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+{
+	struct lpass_variant *v = drvdata->variant;
+	int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map,
+					v->rdma_channels);
+
+	if (chan >= v->rdma_channels)
+		return -EBUSY;
+
+	set_bit(chan, &drvdata->rdma_ch_bit_map);
+
+	return chan;
+}
+
+static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
+{
+	clear_bit(chan, &drvdata->rdma_ch_bit_map);
+
+	return 0;
+}
+
+static int apq8016_lpass_init(struct platform_device *pdev)
+{
+	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	drvdata->pcnoc_mport_clk = devm_clk_get(dev, "pcnoc-mport-clk");
+	if (IS_ERR(drvdata->pcnoc_mport_clk)) {
+		dev_err(&pdev->dev, "%s() error getting pcnoc-mport-clk: %ld\n",
+				__func__, PTR_ERR(drvdata->pcnoc_mport_clk));
+		return PTR_ERR(drvdata->pcnoc_mport_clk);
+	}
+
+	ret = clk_prepare_enable(drvdata->pcnoc_mport_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() Error enabling pcnoc-mport-clk: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	drvdata->pcnoc_sway_clk = devm_clk_get(dev, "pcnoc-sway-clk");
+	if (IS_ERR(drvdata->pcnoc_sway_clk)) {
+		dev_err(&pdev->dev, "%s() error getting pcnoc-sway-clk: %ld\n",
+				__func__, PTR_ERR(drvdata->pcnoc_sway_clk));
+		return PTR_ERR(drvdata->pcnoc_sway_clk);
+	}
+
+	ret = clk_prepare_enable(drvdata->pcnoc_sway_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() Error enabling pcnoc_sway_clk: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int apq8016_lpass_exit(struct platform_device *pdev)
+{
+	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(drvdata->pcnoc_mport_clk);
+	clk_disable_unprepare(drvdata->pcnoc_sway_clk);
+
+	return 0;
+}
+
+
+static struct lpass_variant apq8016_data = {
+	.i2sctrl_reg_base	= 0x1000,
+	.i2sctrl_reg_stride	= 0x1000,
+	.i2s_ports		= 4,
+	.irq_reg_base		= 0x6000,
+	.irq_reg_stride		= 0x1000,
+	.irq_ports		= 3,
+	.rdma_reg_base		= 0x8400,
+	.rdma_reg_stride	= 0x1000,
+	.rdma_channels		= 2,
+	.rdmactl_audif_start	= 1,
+	.dai_driver		= apq8016_lpass_cpu_dai_driver,
+	.num_dai		= ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),
+	.init			= apq8016_lpass_init,
+	.exit			= apq8016_lpass_exit,
+	.alloc_dma_channel	= apq8016_lpass_alloc_dma_channel,
+	.free_dma_channel	= apq8016_lpass_free_dma_channel,
+};
+
+static const struct of_device_id apq8016_lpass_cpu_device_id[] = {
+	{ .compatible = "qcom,lpass-cpu-apq8016", .data = &apq8016_data },
+	{}
+};
+MODULE_DEVICE_TABLE(of, apq8016_lpass_cpu_device_id);
+
+static struct platform_driver apq8016_lpass_cpu_platform_driver = {
+	.driver	= {
+		.name		= "apq8016-lpass-cpu",
+		.of_match_table	= of_match_ptr(apq8016_lpass_cpu_device_id),
+	},
+	.probe	= asoc_qcom_lpass_cpu_platform_probe,
+	.remove	= asoc_qcom_lpass_cpu_platform_remove,
+};
+module_platform_driver(apq8016_lpass_cpu_platform_driver);
+
+MODULE_DESCRIPTION("APQ8016 LPASS CPU Driver");
+MODULE_LICENSE("GPL v2");
+

+ 127 - 113
sound/soc/qcom/lpass-cpu.c

@@ -14,21 +14,17 @@
  */
  */
 
 
 #include <linux/clk.h>
 #include <linux/clk.h>
-#include <linux/compiler.h>
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/ioport.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
-#include <linux/mod_devicetable.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 #include <sound/pcm.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/pcm_params.h>
 #include <linux/regmap.h>
 #include <linux/regmap.h>
 #include <sound/soc.h>
 #include <sound/soc.h>
 #include <sound/soc-dai.h>
 #include <sound/soc-dai.h>
-#include "lpass-lpaif-ipq806x.h"
+#include "lpass-lpaif-reg.h"
 #include "lpass.h"
 #include "lpass.h"
 
 
 static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
@@ -37,7 +33,10 @@ static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	int ret;
 	int ret;
 
 
-	ret = clk_set_rate(drvdata->mi2s_osr_clk, freq);
+	if (IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
+		return 0;
+
+	ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq);
 	if (ret)
 	if (ret)
 		dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n",
 		dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n",
 				__func__, freq, ret);
 				__func__, freq, ret);
@@ -51,18 +50,23 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	int ret;
 	int ret;
 
 
-	ret = clk_prepare_enable(drvdata->mi2s_osr_clk);
-	if (ret) {
-		dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
-				__func__, ret);
-		return ret;
+	if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) {
+		ret = clk_prepare_enable(
+				drvdata->mi2s_osr_clk[dai->driver->id]);
+		if (ret) {
+			dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
+					__func__, ret);
+			return ret;
+		}
 	}
 	}
 
 
-	ret = clk_prepare_enable(drvdata->mi2s_bit_clk);
+	ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]);
 	if (ret) {
 	if (ret) {
 		dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n",
 		dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n",
 				__func__, ret);
 				__func__, ret);
-		clk_disable_unprepare(drvdata->mi2s_osr_clk);
+		if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
+			clk_disable_unprepare(
+				drvdata->mi2s_osr_clk[dai->driver->id]);
 		return ret;
 		return ret;
 	}
 	}
 
 
@@ -74,8 +78,10 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
 {
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 
 
-	clk_disable_unprepare(drvdata->mi2s_bit_clk);
-	clk_disable_unprepare(drvdata->mi2s_osr_clk);
+	clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
+
+	if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id]))
+		clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
 }
 }
 
 
 static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
 static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
@@ -142,14 +148,16 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
 	}
 	}
 
 
 	ret = regmap_write(drvdata->lpaif_map,
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), regval);
+			   LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
+			   regval);
 	if (ret) {
 	if (ret) {
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
 				__func__, ret);
 				__func__, ret);
 		return ret;
 		return ret;
 	}
 	}
 
 
-	ret = clk_set_rate(drvdata->mi2s_bit_clk, rate * bitwidth * 2);
+	ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id],
+			   rate * bitwidth * 2);
 	if (ret) {
 	if (ret) {
 		dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n",
 		dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n",
 				__func__, rate * bitwidth * 2, ret);
 				__func__, rate * bitwidth * 2, ret);
@@ -166,7 +174,8 @@ static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream,
 	int ret;
 	int ret;
 
 
 	ret = regmap_write(drvdata->lpaif_map,
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
+			   LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
+			   0);
 	if (ret)
 	if (ret)
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
 				__func__, ret);
 				__func__, ret);
@@ -181,7 +190,7 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
 	int ret;
 	int ret;
 
 
 	ret = regmap_update_bits(drvdata->lpaif_map,
 	ret = regmap_update_bits(drvdata->lpaif_map,
-			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+			LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
 			LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
 			LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
 	if (ret)
 	if (ret)
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
@@ -201,7 +210,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		ret = regmap_update_bits(drvdata->lpaif_map,
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+				LPAIF_I2SCTL_REG(drvdata->variant,
+						dai->driver->id),
 				LPAIF_I2SCTL_SPKEN_MASK,
 				LPAIF_I2SCTL_SPKEN_MASK,
 				LPAIF_I2SCTL_SPKEN_ENABLE);
 				LPAIF_I2SCTL_SPKEN_ENABLE);
 		if (ret)
 		if (ret)
@@ -212,7 +222,8 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		ret = regmap_update_bits(drvdata->lpaif_map,
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+				LPAIF_I2SCTL_REG(drvdata->variant,
+						dai->driver->id),
 				LPAIF_I2SCTL_SPKEN_MASK,
 				LPAIF_I2SCTL_SPKEN_MASK,
 				LPAIF_I2SCTL_SPKEN_DISABLE);
 				LPAIF_I2SCTL_SPKEN_DISABLE);
 		if (ret)
 		if (ret)
@@ -224,7 +235,7 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 	return ret;
 	return ret;
 }
 }
 
 
-static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
+struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = {
 	.set_sysclk	= lpass_cpu_daiops_set_sysclk,
 	.set_sysclk	= lpass_cpu_daiops_set_sysclk,
 	.startup	= lpass_cpu_daiops_startup,
 	.startup	= lpass_cpu_daiops_startup,
 	.shutdown	= lpass_cpu_daiops_shutdown,
 	.shutdown	= lpass_cpu_daiops_shutdown,
@@ -233,41 +244,23 @@ static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
 	.prepare	= lpass_cpu_daiops_prepare,
 	.prepare	= lpass_cpu_daiops_prepare,
 	.trigger	= lpass_cpu_daiops_trigger,
 	.trigger	= lpass_cpu_daiops_trigger,
 };
 };
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops);
 
 
-static int lpass_cpu_dai_probe(struct snd_soc_dai *dai)
+int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai)
 {
 {
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
 	int ret;
 	int ret;
 
 
 	/* ensure audio hardware is disabled */
 	/* ensure audio hardware is disabled */
 	ret = regmap_write(drvdata->lpaif_map,
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
+			LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0);
 	if (ret)
 	if (ret)
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
 		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
 				__func__, ret);
 				__func__, ret);
 
 
 	return ret;
 	return ret;
 }
 }
-
-static struct snd_soc_dai_driver lpass_cpu_dai_driver = {
-	.playback = {
-		.stream_name	= "lpass-cpu-playback",
-		.formats	= SNDRV_PCM_FMTBIT_S16 |
-					SNDRV_PCM_FMTBIT_S24 |
-					SNDRV_PCM_FMTBIT_S32,
-		.rates		= SNDRV_PCM_RATE_8000 |
-					SNDRV_PCM_RATE_16000 |
-					SNDRV_PCM_RATE_32000 |
-					SNDRV_PCM_RATE_48000 |
-					SNDRV_PCM_RATE_96000,
-		.rate_min	= 8000,
-		.rate_max	= 96000,
-		.channels_min	= 1,
-		.channels_max	= 8,
-	},
-	.probe	= &lpass_cpu_dai_probe,
-	.ops    = &lpass_cpu_dai_ops,
-};
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe);
 
 
 static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
 static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
 	.name = "lpass-cpu",
 	.name = "lpass-cpu",
@@ -275,27 +268,29 @@ static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
 
 
 static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
 static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
 {
 {
+	struct lpass_data *drvdata = dev_get_drvdata(dev);
+	struct lpass_variant *v = drvdata->variant;
 	int i;
 	int i;
 
 
-	for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
-		if (reg == LPAIF_I2SCTL_REG(i))
+	for (i = 0; i < v->i2s_ports; ++i)
+		if (reg == LPAIF_I2SCTL_REG(v, i))
 			return true;
 			return true;
 
 
-	for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
-		if (reg == LPAIF_IRQEN_REG(i))
+	for (i = 0; i < v->irq_ports; ++i) {
+		if (reg == LPAIF_IRQEN_REG(v, i))
 			return true;
 			return true;
-		if (reg == LPAIF_IRQCLEAR_REG(i))
+		if (reg == LPAIF_IRQCLEAR_REG(v, i))
 			return true;
 			return true;
 	}
 	}
 
 
-	for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
-		if (reg == LPAIF_RDMACTL_REG(i))
+	for (i = 0; i < v->rdma_channels; ++i) {
+		if (reg == LPAIF_RDMACTL_REG(v, i))
 			return true;
 			return true;
-		if (reg == LPAIF_RDMABASE_REG(i))
+		if (reg == LPAIF_RDMABASE_REG(v, i))
 			return true;
 			return true;
-		if (reg == LPAIF_RDMABUFF_REG(i))
+		if (reg == LPAIF_RDMABUFF_REG(v, i))
 			return true;
 			return true;
-		if (reg == LPAIF_RDMAPER_REG(i))
+		if (reg == LPAIF_RDMAPER_REG(v, i))
 			return true;
 			return true;
 	}
 	}
 
 
@@ -304,29 +299,31 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
 
 
 static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
 static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
 {
 {
+	struct lpass_data *drvdata = dev_get_drvdata(dev);
+	struct lpass_variant *v = drvdata->variant;
 	int i;
 	int i;
 
 
-	for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
-		if (reg == LPAIF_I2SCTL_REG(i))
+	for (i = 0; i < v->i2s_ports; ++i)
+		if (reg == LPAIF_I2SCTL_REG(v, i))
 			return true;
 			return true;
 
 
-	for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
-		if (reg == LPAIF_IRQEN_REG(i))
+	for (i = 0; i < v->irq_ports; ++i) {
+		if (reg == LPAIF_IRQEN_REG(v, i))
 			return true;
 			return true;
-		if (reg == LPAIF_IRQSTAT_REG(i))
+		if (reg == LPAIF_IRQSTAT_REG(v, i))
 			return true;
 			return true;
 	}
 	}
 
 
-	for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
-		if (reg == LPAIF_RDMACTL_REG(i))
+	for (i = 0; i < v->rdma_channels; ++i) {
+		if (reg == LPAIF_RDMACTL_REG(v, i))
 			return true;
 			return true;
-		if (reg == LPAIF_RDMABASE_REG(i))
+		if (reg == LPAIF_RDMABASE_REG(v, i))
 			return true;
 			return true;
-		if (reg == LPAIF_RDMABUFF_REG(i))
+		if (reg == LPAIF_RDMABUFF_REG(v, i))
 			return true;
 			return true;
-		if (reg == LPAIF_RDMACURR_REG(i))
+		if (reg == LPAIF_RDMACURR_REG(v, i))
 			return true;
 			return true;
-		if (reg == LPAIF_RDMAPER_REG(i))
+		if (reg == LPAIF_RDMAPER_REG(v, i))
 			return true;
 			return true;
 	}
 	}
 
 
@@ -335,36 +332,41 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
 
 
 static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
 static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
 {
 {
+	struct lpass_data *drvdata = dev_get_drvdata(dev);
+	struct lpass_variant *v = drvdata->variant;
 	int i;
 	int i;
 
 
-	for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i)
-		if (reg == LPAIF_IRQSTAT_REG(i))
+	for (i = 0; i < v->irq_ports; ++i)
+		if (reg == LPAIF_IRQSTAT_REG(v, i))
 			return true;
 			return true;
 
 
-	for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i)
-		if (reg == LPAIF_RDMACURR_REG(i))
+	for (i = 0; i < v->rdma_channels; ++i)
+		if (reg == LPAIF_RDMACURR_REG(v, i))
 			return true;
 			return true;
 
 
 	return false;
 	return false;
 }
 }
 
 
-static const struct regmap_config lpass_cpu_regmap_config = {
+static struct regmap_config lpass_cpu_regmap_config = {
 	.reg_bits = 32,
 	.reg_bits = 32,
 	.reg_stride = 4,
 	.reg_stride = 4,
 	.val_bits = 32,
 	.val_bits = 32,
-	.max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX),
 	.writeable_reg = lpass_cpu_regmap_writeable,
 	.writeable_reg = lpass_cpu_regmap_writeable,
 	.readable_reg = lpass_cpu_regmap_readable,
 	.readable_reg = lpass_cpu_regmap_readable,
 	.volatile_reg = lpass_cpu_regmap_volatile,
 	.volatile_reg = lpass_cpu_regmap_volatile,
 	.cache_type = REGCACHE_FLAT,
 	.cache_type = REGCACHE_FLAT,
 };
 };
 
 
-static int lpass_cpu_platform_probe(struct platform_device *pdev)
+int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
 {
 {
 	struct lpass_data *drvdata;
 	struct lpass_data *drvdata;
 	struct device_node *dsp_of_node;
 	struct device_node *dsp_of_node;
 	struct resource *res;
 	struct resource *res;
-	int ret;
+	struct lpass_variant *variant;
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	char clk_name[16];
+	int ret, i, dai_id;
 
 
 	dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
 	dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
 	if (dsp_of_node) {
 	if (dsp_of_node) {
@@ -379,11 +381,14 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
 		return -ENOMEM;
 		return -ENOMEM;
 	platform_set_drvdata(pdev, drvdata);
 	platform_set_drvdata(pdev, drvdata);
 
 
+	match = of_match_device(dev->driver->of_match_table, dev);
+	if (!match || !match->data)
+		return -EINVAL;
+
+	drvdata->variant = (struct lpass_variant *)match->data;
+	variant = drvdata->variant;
+
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
-	if (!res) {
-		dev_err(&pdev->dev, "%s() error getting resource\n", __func__);
-		return -ENODEV;
-	}
 
 
 	drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
 	drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR((void const __force *)drvdata->lpaif)) {
 	if (IS_ERR((void const __force *)drvdata->lpaif)) {
@@ -393,6 +398,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
 		return PTR_ERR((void const __force *)drvdata->lpaif);
 		return PTR_ERR((void const __force *)drvdata->lpaif);
 	}
 	}
 
 
+	lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant,
+						variant->rdma_channels);
+
 	drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
 	drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
 			&lpass_cpu_regmap_config);
 			&lpass_cpu_regmap_config);
 	if (IS_ERR(drvdata->lpaif_map)) {
 	if (IS_ERR(drvdata->lpaif_map)) {
@@ -401,18 +409,38 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
 		return PTR_ERR(drvdata->lpaif_map);
 		return PTR_ERR(drvdata->lpaif_map);
 	}
 	}
 
 
-	drvdata->mi2s_osr_clk = devm_clk_get(&pdev->dev, "mi2s-osr-clk");
-	if (IS_ERR(drvdata->mi2s_osr_clk)) {
-		dev_err(&pdev->dev, "%s() error getting mi2s-osr-clk: %ld\n",
-				__func__, PTR_ERR(drvdata->mi2s_osr_clk));
-		return PTR_ERR(drvdata->mi2s_osr_clk);
-	}
-
-	drvdata->mi2s_bit_clk = devm_clk_get(&pdev->dev, "mi2s-bit-clk");
-	if (IS_ERR(drvdata->mi2s_bit_clk)) {
-		dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n",
-				__func__, PTR_ERR(drvdata->mi2s_bit_clk));
-		return PTR_ERR(drvdata->mi2s_bit_clk);
+	if (variant->init)
+		variant->init(pdev);
+
+	for (i = 0; i < variant->num_dai; i++) {
+		dai_id = variant->dai_driver[i].id;
+		if (variant->num_dai > 1)
+			sprintf(clk_name, "mi2s-osr-clk%d", i);
+		else
+			sprintf(clk_name, "mi2s-osr-clk");
+
+		drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev,
+								clk_name);
+		if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
+			dev_warn(&pdev->dev,
+				"%s() error getting mi2s-osr-clk: %ld\n",
+				__func__,
+				PTR_ERR(drvdata->mi2s_osr_clk[dai_id]));
+		}
+
+		if (variant->num_dai > 1)
+			sprintf(clk_name, "mi2s-bit-clk%d", i);
+		else
+			sprintf(clk_name, "mi2s-bit-clk");
+
+		drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev,
+							    clk_name);
+		if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
+			dev_err(&pdev->dev,
+				"%s() error getting mi2s-bit-clk: %ld\n",
+				__func__, PTR_ERR(drvdata->mi2s_bit_clk[i]));
+			return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
+		}
 	}
 	}
 
 
 	drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");
 	drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");
@@ -439,7 +467,9 @@ static int lpass_cpu_platform_probe(struct platform_device *pdev)
 	}
 	}
 
 
 	ret = devm_snd_soc_register_component(&pdev->dev,
 	ret = devm_snd_soc_register_component(&pdev->dev,
-			&lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1);
+					      &lpass_cpu_comp_driver,
+					      variant->dai_driver,
+					      variant->num_dai);
 	if (ret) {
 	if (ret) {
 		dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n",
 		dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n",
 				__func__, ret);
 				__func__, ret);
@@ -459,33 +489,17 @@ err_clk:
 	clk_disable_unprepare(drvdata->ahbix_clk);
 	clk_disable_unprepare(drvdata->ahbix_clk);
 	return ret;
 	return ret;
 }
 }
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe);
 
 
-static int lpass_cpu_platform_remove(struct platform_device *pdev)
+int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev)
 {
 {
 	struct lpass_data *drvdata = platform_get_drvdata(pdev);
 	struct lpass_data *drvdata = platform_get_drvdata(pdev);
 
 
+	if (drvdata->variant->exit)
+		drvdata->variant->exit(pdev);
+
 	clk_disable_unprepare(drvdata->ahbix_clk);
 	clk_disable_unprepare(drvdata->ahbix_clk);
 
 
 	return 0;
 	return 0;
 }
 }
-
-#ifdef CONFIG_OF
-static const struct of_device_id lpass_cpu_device_id[] = {
-	{ .compatible = "qcom,lpass-cpu" },
-	{}
-};
-MODULE_DEVICE_TABLE(of, lpass_cpu_device_id);
-#endif
-
-static struct platform_driver lpass_cpu_platform_driver = {
-	.driver	= {
-		.name		= "lpass-cpu",
-		.of_match_table	= of_match_ptr(lpass_cpu_device_id),
-	},
-	.probe	= lpass_cpu_platform_probe,
-	.remove	= lpass_cpu_platform_remove,
-};
-module_platform_driver(lpass_cpu_platform_driver);
-
-MODULE_DESCRIPTION("QTi LPASS CPU Driver");
-MODULE_LICENSE("GPL v2");
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove);

+ 109 - 0
sound/soc/qcom/lpass-ipq806x.c

@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * lpass-ipq806x.c -- ALSA SoC CPU DAI driver for QTi LPASS
+ * Splited out the IPQ8064 soc specific from lpass-cpu.c
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "lpass-lpaif-reg.h"
+#include "lpass.h"
+
+enum lpaif_i2s_ports {
+	IPQ806X_LPAIF_I2S_PORT_CODEC_SPK,
+	IPQ806X_LPAIF_I2S_PORT_CODEC_MIC,
+	IPQ806X_LPAIF_I2S_PORT_SEC_SPK,
+	IPQ806X_LPAIF_I2S_PORT_SEC_MIC,
+	IPQ806X_LPAIF_I2S_PORT_MI2S,
+};
+
+enum lpaif_dma_channels {
+	IPQ806X_LPAIF_RDMA_CHAN_MI2S,
+	IPQ806X_LPAIF_RDMA_CHAN_PCM0,
+	IPQ806X_LPAIF_RDMA_CHAN_PCM1,
+};
+
+static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = {
+	.id	= IPQ806X_LPAIF_I2S_PORT_MI2S,
+	.playback = {
+		.stream_name	= "lpass-cpu-playback",
+		.formats	= SNDRV_PCM_FMTBIT_S16 |
+					SNDRV_PCM_FMTBIT_S24 |
+					SNDRV_PCM_FMTBIT_S32,
+		.rates		= SNDRV_PCM_RATE_8000 |
+					SNDRV_PCM_RATE_16000 |
+					SNDRV_PCM_RATE_32000 |
+					SNDRV_PCM_RATE_48000 |
+					SNDRV_PCM_RATE_96000,
+		.rate_min	= 8000,
+		.rate_max	= 96000,
+		.channels_min	= 1,
+		.channels_max	= 8,
+	},
+	.probe	= &asoc_qcom_lpass_cpu_dai_probe,
+	.ops    = &asoc_qcom_lpass_cpu_dai_ops,
+};
+
+static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+{
+	return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
+}
+
+static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
+{
+	return 0;
+}
+
+struct lpass_variant ipq806x_data = {
+	.i2sctrl_reg_base	= 0x0010,
+	.i2sctrl_reg_stride	= 0x04,
+	.i2s_ports		= 5,
+	.irq_reg_base		= 0x3000,
+	.irq_reg_stride		= 0x1000,
+	.irq_ports		= 3,
+	.rdma_reg_base		= 0x6000,
+	.rdma_reg_stride	= 0x1000,
+	.rdma_channels		= 4,
+	.dai_driver		= &ipq806x_lpass_cpu_dai_driver,
+	.num_dai		= 1,
+	.alloc_dma_channel	= ipq806x_lpass_alloc_dma_channel,
+	.free_dma_channel	= ipq806x_lpass_free_dma_channel,
+};
+
+static const struct of_device_id ipq806x_lpass_cpu_device_id[] = {
+	{ .compatible = "qcom,lpass-cpu", .data = &ipq806x_data },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ipq806x_lpass_cpu_device_id);
+
+static struct platform_driver ipq806x_lpass_cpu_platform_driver = {
+	.driver	= {
+		.name		= "lpass-cpu",
+		.of_match_table	= of_match_ptr(ipq806x_lpass_cpu_device_id),
+	},
+	.probe	= asoc_qcom_lpass_cpu_platform_probe,
+	.remove	= asoc_qcom_lpass_cpu_platform_remove,
+};
+module_platform_driver(ipq806x_lpass_cpu_platform_driver);
+
+MODULE_DESCRIPTION("QTi LPASS CPU Driver");
+MODULE_LICENSE("GPL v2");

+ 23 - 69
sound/soc/qcom/lpass-lpaif-ipq806x.h → sound/soc/qcom/lpass-lpaif-reg.h

@@ -9,37 +9,17 @@
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  * GNU General Public License for more details.
- *
- * lpass-lpaif-ipq806x.h -- Definitions for the QTi LPAIF in the ipq806x LPASS
  */
  */
 
 
-#ifndef __LPASS_LPAIF_H__
-#define __LPASS_LPAIF_H__
-
-#define LPAIF_BANK_OFFSET		0x1000
+#ifndef __LPASS_LPAIF_REG_H__
+#define __LPASS_LPAIF_REG_H__
 
 
 /* LPAIF I2S */
 /* LPAIF I2S */
 
 
-#define LPAIF_I2SCTL_REG_BASE		0x0010
-#define LPAIF_I2SCTL_REG_STRIDE		0x4
-#define LPAIF_I2SCTL_REG_ADDR(addr, port) \
-	(LPAIF_I2SCTL_REG_BASE + (addr) + (LPAIF_I2SCTL_REG_STRIDE * (port)))
-
-enum lpaif_i2s_ports {
-	LPAIF_I2S_PORT_MIN		= 0,
-
-	LPAIF_I2S_PORT_CODEC_SPK	= 0,
-	LPAIF_I2S_PORT_CODEC_MIC	= 1,
-	LPAIF_I2S_PORT_SEC_SPK		= 2,
-	LPAIF_I2S_PORT_SEC_MIC		= 3,
-	LPAIF_I2S_PORT_MI2S		= 4,
-
-	LPAIF_I2S_PORT_MAX		= 4,
-	LPAIF_I2S_PORT_NUM		= 5,
-};
-
-#define LPAIF_I2SCTL_REG(port)		LPAIF_I2SCTL_REG_ADDR(0x0, (port))
+#define LPAIF_I2SCTL_REG_ADDR(v, addr, port) \
+	(v->i2sctrl_reg_base + (addr) + v->i2sctrl_reg_stride * (port))
 
 
+#define LPAIF_I2SCTL_REG(v, port)	LPAIF_I2SCTL_REG_ADDR(v, 0x0, (port))
 #define LPAIF_I2SCTL_LOOPBACK_MASK	0x8000
 #define LPAIF_I2SCTL_LOOPBACK_MASK	0x8000
 #define LPAIF_I2SCTL_LOOPBACK_SHIFT	15
 #define LPAIF_I2SCTL_LOOPBACK_SHIFT	15
 #define LPAIF_I2SCTL_LOOPBACK_DISABLE	(0 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
 #define LPAIF_I2SCTL_LOOPBACK_DISABLE	(0 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
@@ -79,55 +59,36 @@ enum lpaif_i2s_ports {
 #define LPAIF_I2SCTL_BITWIDTH_32	(2 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
 #define LPAIF_I2SCTL_BITWIDTH_32	(2 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
 
 
 /* LPAIF IRQ */
 /* LPAIF IRQ */
+#define LPAIF_IRQ_REG_ADDR(v, addr, port) \
+	(v->irq_reg_base + (addr) + v->irq_reg_stride * (port))
 
 
-#define LPAIF_IRQ_REG_BASE		0x3000
-#define LPAIF_IRQ_REG_STRIDE		0x1000
-#define LPAIF_IRQ_REG_ADDR(addr, port) \
-	(LPAIF_IRQ_REG_BASE + (addr) + (LPAIF_IRQ_REG_STRIDE * (port)))
-
-enum lpaif_irq_ports {
-	LPAIF_IRQ_PORT_MIN		= 0,
+#define LPAIF_IRQ_PORT_HOST		0
 
 
-	LPAIF_IRQ_PORT_HOST		= 0,
-	LPAIF_IRQ_PORT_ADSP		= 1,
-
-	LPAIF_IRQ_PORT_MAX		= 2,
-	LPAIF_IRQ_PORT_NUM		= 3,
-};
-
-#define LPAIF_IRQEN_REG(port)		LPAIF_IRQ_REG_ADDR(0x0, (port))
-#define LPAIF_IRQSTAT_REG(port)		LPAIF_IRQ_REG_ADDR(0x4, (port))
-#define LPAIF_IRQCLEAR_REG(port)	LPAIF_IRQ_REG_ADDR(0xC, (port))
+#define LPAIF_IRQEN_REG(v, port)	LPAIF_IRQ_REG_ADDR(v, 0x0, (port))
+#define LPAIF_IRQSTAT_REG(v, port)	LPAIF_IRQ_REG_ADDR(v, 0x4, (port))
+#define LPAIF_IRQCLEAR_REG(v, port)	LPAIF_IRQ_REG_ADDR(v, 0xC, (port))
 
 
 #define LPAIF_IRQ_BITSTRIDE		3
 #define LPAIF_IRQ_BITSTRIDE		3
+
 #define LPAIF_IRQ_PER(chan)		(1 << (LPAIF_IRQ_BITSTRIDE * (chan)))
 #define LPAIF_IRQ_PER(chan)		(1 << (LPAIF_IRQ_BITSTRIDE * (chan)))
 #define LPAIF_IRQ_XRUN(chan)		(2 << (LPAIF_IRQ_BITSTRIDE * (chan)))
 #define LPAIF_IRQ_XRUN(chan)		(2 << (LPAIF_IRQ_BITSTRIDE * (chan)))
 #define LPAIF_IRQ_ERR(chan)		(4 << (LPAIF_IRQ_BITSTRIDE * (chan)))
 #define LPAIF_IRQ_ERR(chan)		(4 << (LPAIF_IRQ_BITSTRIDE * (chan)))
+
 #define LPAIF_IRQ_ALL(chan)		(7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
 #define LPAIF_IRQ_ALL(chan)		(7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
 
 
 /* LPAIF DMA */
 /* LPAIF DMA */
 
 
-#define LPAIF_RDMA_REG_BASE		0x6000
-#define LPAIF_RDMA_REG_STRIDE		0x1000
-#define LPAIF_RDMA_REG_ADDR(addr, chan) \
-	(LPAIF_RDMA_REG_BASE + (addr) + (LPAIF_RDMA_REG_STRIDE * (chan)))
-
-enum lpaif_dma_channels {
-	LPAIF_RDMA_CHAN_MIN		= 0,
-
-	LPAIF_RDMA_CHAN_MI2S		= 0,
-	LPAIF_RDMA_CHAN_PCM0		= 1,
-	LPAIF_RDMA_CHAN_PCM1		= 2,
+#define LPAIF_RDMA_REG_ADDR(v, addr, chan) \
+	(v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan))
 
 
-	LPAIF_RDMA_CHAN_MAX		= 4,
-	LPAIF_RDMA_CHAN_NUM		= 5,
-};
+#define LPAIF_RDMACTL_AUDINTF(id)	(id << LPAIF_RDMACTL_AUDINTF_SHIFT)
 
 
-#define LPAIF_RDMACTL_REG(chan)		LPAIF_RDMA_REG_ADDR(0x00, (chan))
-#define LPAIF_RDMABASE_REG(chan)	LPAIF_RDMA_REG_ADDR(0x04, (chan))
-#define	LPAIF_RDMABUFF_REG(chan)	LPAIF_RDMA_REG_ADDR(0x08, (chan))
-#define LPAIF_RDMACURR_REG(chan)	LPAIF_RDMA_REG_ADDR(0x0C, (chan))
-#define	LPAIF_RDMAPER_REG(chan)		LPAIF_RDMA_REG_ADDR(0x10, (chan))
+#define LPAIF_RDMACTL_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x00, (chan))
+#define LPAIF_RDMABASE_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x04, (chan))
+#define	LPAIF_RDMABUFF_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x08, (chan))
+#define LPAIF_RDMACURR_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x0C, (chan))
+#define	LPAIF_RDMAPER_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x10, (chan))
+#define	LPAIF_RDMAPERCNT_REG(v, chan)	LPAIF_RDMA_REG_ADDR(v, 0x14, (chan))
 
 
 #define LPAIF_RDMACTL_BURSTEN_MASK	0x800
 #define LPAIF_RDMACTL_BURSTEN_MASK	0x800
 #define LPAIF_RDMACTL_BURSTEN_SHIFT	11
 #define LPAIF_RDMACTL_BURSTEN_SHIFT	11
@@ -145,13 +106,6 @@ enum lpaif_dma_channels {
 
 
 #define LPAIF_RDMACTL_AUDINTF_MASK	0x0F0
 #define LPAIF_RDMACTL_AUDINTF_MASK	0x0F0
 #define LPAIF_RDMACTL_AUDINTF_SHIFT	4
 #define LPAIF_RDMACTL_AUDINTF_SHIFT	4
-#define LPAIF_RDMACTL_AUDINTF_NONE	(0 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_CODEC	(1 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_PCM	(2 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_SEC_I2S	(3 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_MI2S	(4 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_HDMI	(5 << LPAIF_RDMACTL_AUDINTF_SHIFT)
-#define LPAIF_RDMACTL_AUDINTF_SEC_PCM	(7 << LPAIF_RDMACTL_AUDINTF_SHIFT)
 
 
 #define LPAIF_RDMACTL_FIFOWM_MASK	0x00E
 #define LPAIF_RDMACTL_FIFOWM_MASK	0x00E
 #define LPAIF_RDMACTL_FIFOWM_SHIFT	1
 #define LPAIF_RDMACTL_FIFOWM_SHIFT	1
@@ -169,4 +123,4 @@ enum lpaif_dma_channels {
 #define LPAIF_RDMACTL_ENABLE_OFF	(0 << LPAIF_RDMACTL_ENABLE_SHIFT)
 #define LPAIF_RDMACTL_ENABLE_OFF	(0 << LPAIF_RDMACTL_ENABLE_SHIFT)
 #define LPAIF_RDMACTL_ENABLE_ON		(1 << LPAIF_RDMACTL_ENABLE_SHIFT)
 #define LPAIF_RDMACTL_ENABLE_ON		(1 << LPAIF_RDMACTL_ENABLE_SHIFT)
 
 
-#endif /* __LPASS_LPAIF_H__ */
+#endif /* __LPASS_LPAIF_REG_H__ */

+ 131 - 71
sound/soc/qcom/lpass-platform.c

@@ -13,23 +13,22 @@
  * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
  * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
  */
  */
 
 
-#include <linux/compiler.h>
-#include <linux/device.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-mapping.h>
-#include <linux/err.h>
 #include <linux/export.h>
 #include <linux/export.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/module.h>
-#include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
-#include <sound/memalloc.h>
-#include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/pcm_params.h>
 #include <linux/regmap.h>
 #include <linux/regmap.h>
 #include <sound/soc.h>
 #include <sound/soc.h>
-#include "lpass-lpaif-ipq806x.h"
+#include "lpass-lpaif-reg.h"
 #include "lpass.h"
 #include "lpass.h"
 
 
+struct lpass_pcm_data {
+	int rdma_ch;
+	int i2s_port;
+};
+
 #define LPASS_PLATFORM_BUFFER_SIZE	(16 * 1024)
 #define LPASS_PLATFORM_BUFFER_SIZE	(16 * 1024)
 #define LPASS_PLATFORM_PERIODS		2
 #define LPASS_PLATFORM_PERIODS		2
 
 
@@ -84,13 +83,15 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
 		struct snd_pcm_hw_params *params)
 		struct snd_pcm_hw_params *params)
 {
 {
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_variant *v = drvdata->variant;
 	snd_pcm_format_t format = params_format(params);
 	snd_pcm_format_t format = params_format(params);
 	unsigned int channels = params_channels(params);
 	unsigned int channels = params_channels(params);
 	unsigned int regval;
 	unsigned int regval;
 	int bitwidth;
 	int bitwidth;
-	int ret;
+	int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start;
 
 
 	bitwidth = snd_pcm_format_width(format);
 	bitwidth = snd_pcm_format_width(format);
 	if (bitwidth < 0) {
 	if (bitwidth < 0) {
@@ -100,7 +101,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
 	}
 	}
 
 
 	regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
 	regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
-			LPAIF_RDMACTL_AUDINTF_MI2S |
+			LPAIF_RDMACTL_AUDINTF(rdma_port) |
 			LPAIF_RDMACTL_FIFOWM_8;
 			LPAIF_RDMACTL_FIFOWM_8;
 
 
 	switch (bitwidth) {
 	switch (bitwidth) {
@@ -156,7 +157,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
 	}
 	}
 
 
 	ret = regmap_write(drvdata->lpaif_map,
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval);
+			LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval);
 	if (ret) {
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 				__func__, ret);
 				__func__, ret);
@@ -169,12 +170,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
 static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
 static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
 {
 {
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_variant *v = drvdata->variant;
 	int ret;
 	int ret;
 
 
 	ret = regmap_write(drvdata->lpaif_map,
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
+			LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0);
 	if (ret)
 	if (ret)
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 				__func__, ret);
 				__func__, ret);
@@ -186,12 +189,14 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
 {
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
-	int ret;
+	struct lpass_variant *v = drvdata->variant;
+	int ret, ch = pcm_data->rdma_ch;
 
 
 	ret = regmap_write(drvdata->lpaif_map,
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S),
+			LPAIF_RDMABASE_REG(v, ch),
 			runtime->dma_addr);
 			runtime->dma_addr);
 	if (ret) {
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
 		dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
@@ -200,7 +205,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
 	}
 	}
 
 
 	ret = regmap_write(drvdata->lpaif_map,
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S),
+			LPAIF_RDMABUFF_REG(v, ch),
 			(snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
 			(snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
 	if (ret) {
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
 		dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
@@ -209,7 +214,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
 	}
 	}
 
 
 	ret = regmap_write(drvdata->lpaif_map,
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S),
+			LPAIF_RDMAPER_REG(v, ch),
 			(snd_pcm_lib_period_bytes(substream) >> 2) - 1);
 			(snd_pcm_lib_period_bytes(substream) >> 2) - 1);
 	if (ret) {
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
 		dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
@@ -218,7 +223,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
 	}
 	}
 
 
 	ret = regmap_update_bits(drvdata->lpaif_map,
 	ret = regmap_update_bits(drvdata->lpaif_map,
-			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+			LPAIF_RDMACTL_REG(v, ch),
 			LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
 			LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
 	if (ret) {
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
@@ -233,9 +238,11 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 		int cmd)
 		int cmd)
 {
 {
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
-	int ret;
+	struct lpass_variant *v = drvdata->variant;
+	int ret, ch = pcm_data->rdma_ch;
 
 
 	switch (cmd) {
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_START:
@@ -243,8 +250,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		/* clear status before enabling interrupts */
 		/* clear status before enabling interrupts */
 		ret = regmap_write(drvdata->lpaif_map,
 		ret = regmap_write(drvdata->lpaif_map,
-				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ALL(ch));
 		if (ret) {
 		if (ret) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 					__func__, ret);
 					__func__, ret);
@@ -252,9 +259,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 		}
 		}
 
 
 		ret = regmap_update_bits(drvdata->lpaif_map,
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S),
-				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ALL(ch),
+				LPAIF_IRQ_ALL(ch));
 		if (ret) {
 		if (ret) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
 			dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
 					__func__, ret);
 					__func__, ret);
@@ -262,7 +269,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 		}
 		}
 
 
 		ret = regmap_update_bits(drvdata->lpaif_map,
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+				LPAIF_RDMACTL_REG(v, ch),
 				LPAIF_RDMACTL_ENABLE_MASK,
 				LPAIF_RDMACTL_ENABLE_MASK,
 				LPAIF_RDMACTL_ENABLE_ON);
 				LPAIF_RDMACTL_ENABLE_ON);
 		if (ret) {
 		if (ret) {
@@ -275,7 +282,7 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		ret = regmap_update_bits(drvdata->lpaif_map,
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+				LPAIF_RDMACTL_REG(v, ch),
 				LPAIF_RDMACTL_ENABLE_MASK,
 				LPAIF_RDMACTL_ENABLE_MASK,
 				LPAIF_RDMACTL_ENABLE_OFF);
 				LPAIF_RDMACTL_ENABLE_OFF);
 		if (ret) {
 		if (ret) {
@@ -285,8 +292,8 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
 		}
 		}
 
 
 		ret = regmap_update_bits(drvdata->lpaif_map,
 		ret = regmap_update_bits(drvdata->lpaif_map,
-				LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0);
+				LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ALL(ch), 0);
 		if (ret) {
 		if (ret) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
 			dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
 					__func__, ret);
 					__func__, ret);
@@ -302,13 +309,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
 		struct snd_pcm_substream *substream)
 		struct snd_pcm_substream *substream)
 {
 {
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
 	struct lpass_data *drvdata =
 	struct lpass_data *drvdata =
 			snd_soc_platform_get_drvdata(soc_runtime->platform);
 			snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_variant *v = drvdata->variant;
 	unsigned int base_addr, curr_addr;
 	unsigned int base_addr, curr_addr;
-	int ret;
+	int ret, ch = pcm_data->rdma_ch;
 
 
 	ret = regmap_read(drvdata->lpaif_map,
 	ret = regmap_read(drvdata->lpaif_map,
-			LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr);
+			LPAIF_RDMABASE_REG(v, ch), &base_addr);
 	if (ret) {
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
 		dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
 				__func__, ret);
 				__func__, ret);
@@ -316,7 +325,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
 	}
 	}
 
 
 	ret = regmap_read(drvdata->lpaif_map,
 	ret = regmap_read(drvdata->lpaif_map,
-			LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr);
+			LPAIF_RDMACURR_REG(v, ch), &curr_addr);
 	if (ret) {
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
 		dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
 				__func__, ret);
 				__func__, ret);
@@ -347,29 +356,20 @@ static struct snd_pcm_ops lpass_platform_pcm_ops = {
 	.mmap		= lpass_platform_pcmops_mmap,
 	.mmap		= lpass_platform_pcmops_mmap,
 };
 };
 
 
-static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
+static irqreturn_t lpass_dma_interrupt_handler(
+			struct snd_pcm_substream *substream,
+			struct lpass_data *drvdata,
+			int chan, u32 interrupts)
 {
 {
-	struct snd_pcm_substream *substream = data;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
-	struct lpass_data *drvdata =
-		snd_soc_platform_get_drvdata(soc_runtime->platform);
-	unsigned int interrupts;
+	struct lpass_variant *v = drvdata->variant;
 	irqreturn_t ret = IRQ_NONE;
 	irqreturn_t ret = IRQ_NONE;
 	int rv;
 	int rv;
 
 
-	rv = regmap_read(drvdata->lpaif_map,
-			LPAIF_IRQSTAT_REG(LPAIF_IRQ_PORT_HOST), &interrupts);
-	if (rv) {
-		dev_err(soc_runtime->dev, "%s() error reading from irqstat reg: %d\n",
-				__func__, rv);
-		return IRQ_NONE;
-	}
-	interrupts &= LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S);
-
-	if (interrupts & LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)) {
+	if (interrupts & LPAIF_IRQ_PER(chan)) {
 		rv = regmap_write(drvdata->lpaif_map,
 		rv = regmap_write(drvdata->lpaif_map,
-				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_PER(chan));
 		if (rv) {
 		if (rv) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 					__func__, rv);
 					__func__, rv);
@@ -379,10 +379,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 		ret = IRQ_HANDLED;
 		ret = IRQ_HANDLED;
 	}
 	}
 
 
-	if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) {
+	if (interrupts & LPAIF_IRQ_XRUN(chan)) {
 		rv = regmap_write(drvdata->lpaif_map,
 		rv = regmap_write(drvdata->lpaif_map,
-				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_XRUN(chan));
 		if (rv) {
 		if (rv) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 					__func__, rv);
 					__func__, rv);
@@ -393,10 +393,10 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 		ret = IRQ_HANDLED;
 		ret = IRQ_HANDLED;
 	}
 	}
 
 
-	if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) {
+	if (interrupts & LPAIF_IRQ_ERR(chan)) {
 		rv = regmap_write(drvdata->lpaif_map,
 		rv = regmap_write(drvdata->lpaif_map,
-				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
-				LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S));
+				LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ERR(chan));
 		if (rv) {
 		if (rv) {
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
 					__func__, rv);
 					__func__, rv);
@@ -410,6 +410,35 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
 	return ret;
 	return ret;
 }
 }
 
 
+static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
+{
+	struct lpass_data *drvdata = data;
+	struct lpass_variant *v = drvdata->variant;
+	unsigned int irqs;
+	int rv, chan;
+
+	rv = regmap_read(drvdata->lpaif_map,
+			LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
+	if (rv) {
+		pr_err("%s() error reading from irqstat reg: %d\n",
+				__func__, rv);
+		return IRQ_NONE;
+	}
+
+	/* Handle per channel interrupts */
+	for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
+		if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
+			rv = lpass_dma_interrupt_handler(
+						drvdata->substream[chan],
+						drvdata, chan, irqs);
+			if (rv != IRQ_HANDLED)
+				return rv;
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
 static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
 static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
 		struct snd_soc_pcm_runtime *soc_runtime)
 		struct snd_soc_pcm_runtime *soc_runtime)
 {
 {
@@ -448,9 +477,27 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
 	struct snd_pcm *pcm = soc_runtime->pcm;
 	struct snd_pcm *pcm = soc_runtime->pcm;
 	struct snd_pcm_substream *substream =
 	struct snd_pcm_substream *substream =
 		pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
 		pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
 	struct lpass_data *drvdata =
 	struct lpass_data *drvdata =
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
 		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_variant *v = drvdata->variant;
 	int ret;
 	int ret;
+	struct lpass_pcm_data *data;
+
+	data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	if (v->alloc_dma_channel)
+		data->rdma_ch = v->alloc_dma_channel(drvdata);
+
+	if (IS_ERR_VALUE(data->rdma_ch))
+		return data->rdma_ch;
+
+	drvdata->substream[data->rdma_ch] = substream;
+	data->i2s_port = cpu_dai->driver->id;
+
+	snd_soc_pcm_set_drvdata(soc_runtime, data);
 
 
 	soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32);
 	soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32);
 	soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask;
 	soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask;
@@ -459,29 +506,12 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 
 
-	ret = devm_request_irq(soc_runtime->dev, drvdata->lpaif_irq,
-			lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
-			"lpass-irq-lpaif", substream);
-	if (ret) {
-		dev_err(soc_runtime->dev, "%s() irq request failed: %d\n",
-				__func__, ret);
-		goto err_buf;
-	}
-
-	/* ensure audio hardware is disabled */
 	ret = regmap_write(drvdata->lpaif_map,
 	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), 0);
-	if (ret) {
-		dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
-				__func__, ret);
-		return ret;
-	}
-	ret = regmap_write(drvdata->lpaif_map,
-			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
+			LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
 	if (ret) {
 	if (ret) {
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
 				__func__, ret);
 				__func__, ret);
-		return ret;
+		goto err_buf;
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -496,6 +526,15 @@ static void lpass_platform_pcm_free(struct snd_pcm *pcm)
 	struct snd_pcm_substream *substream =
 	struct snd_pcm_substream *substream =
 		pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
 		pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
 	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_data *drvdata =
+		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime);
+	struct lpass_variant *v = drvdata->variant;
+
+	drvdata->substream[data->rdma_ch] = NULL;
+
+	if (v->free_dma_channel)
+		v->free_dma_channel(drvdata, data->rdma_ch);
 
 
 	lpass_platform_free_buffer(substream, soc_runtime);
 	lpass_platform_free_buffer(substream, soc_runtime);
 }
 }
@@ -509,6 +548,8 @@ static struct snd_soc_platform_driver lpass_platform_driver = {
 int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
 int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
 {
 {
 	struct lpass_data *drvdata = platform_get_drvdata(pdev);
 	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+	struct lpass_variant *v = drvdata->variant;
+	int ret;
 
 
 	drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
 	drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
 	if (drvdata->lpaif_irq < 0) {
 	if (drvdata->lpaif_irq < 0) {
@@ -517,6 +558,25 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 
 
+	/* ensure audio hardware is disabled */
+	ret = regmap_write(drvdata->lpaif_map,
+			LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() error writing to irqen reg: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
+			lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
+			"lpass-irq-lpaif", drvdata);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() irq request failed: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+
 	return devm_snd_soc_register_platform(&pdev->dev,
 	return devm_snd_soc_register_platform(&pdev->dev,
 			&lpass_platform_driver);
 			&lpass_platform_driver);
 }
 }

+ 49 - 2
sound/soc/qcom/lpass.h

@@ -22,6 +22,8 @@
 #include <linux/regmap.h>
 #include <linux/regmap.h>
 
 
 #define LPASS_AHBIX_CLOCK_FREQUENCY		131072000
 #define LPASS_AHBIX_CLOCK_FREQUENCY		131072000
+#define LPASS_MAX_MI2S_PORTS			(8)
+#define LPASS_MAX_DMA_CHANNELS			(8)
 
 
 /* Both the CPU DAI and platform drivers will access this data */
 /* Both the CPU DAI and platform drivers will access this data */
 struct lpass_data {
 struct lpass_data {
@@ -30,10 +32,10 @@ struct lpass_data {
 	struct clk *ahbix_clk;
 	struct clk *ahbix_clk;
 
 
 	/* MI2S system clock */
 	/* MI2S system clock */
-	struct clk *mi2s_osr_clk;
+	struct clk *mi2s_osr_clk[LPASS_MAX_MI2S_PORTS];
 
 
 	/* MI2S bit clock (derived from system clock by a divider */
 	/* MI2S bit clock (derived from system clock by a divider */
-	struct clk *mi2s_bit_clk;
+	struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS];
 
 
 	/* low-power audio interface (LPAIF) registers */
 	/* low-power audio interface (LPAIF) registers */
 	void __iomem *lpaif;
 	void __iomem *lpaif;
@@ -43,9 +45,54 @@ struct lpass_data {
 
 
 	/* interrupts from the low-power audio interface (LPAIF) */
 	/* interrupts from the low-power audio interface (LPAIF) */
 	int lpaif_irq;
 	int lpaif_irq;
+
+	/* SOC specific variations in the LPASS IP integration */
+	struct lpass_variant *variant;
+
+	/* bit map to keep track of static channel allocations */
+	unsigned long rdma_ch_bit_map;
+
+	/* used it for handling interrupt per dma channel */
+	struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
+
+	/* 8016 specific */
+	struct clk *pcnoc_mport_clk;
+	struct clk *pcnoc_sway_clk;
+};
+
+/* Vairant data per each SOC */
+struct lpass_variant {
+	u32	i2sctrl_reg_base;
+	u32	i2sctrl_reg_stride;
+	u32	i2s_ports;
+	u32	irq_reg_base;
+	u32	irq_reg_stride;
+	u32	irq_ports;
+	u32	rdma_reg_base;
+	u32	rdma_reg_stride;
+	u32	rdma_channels;
+
+	/**
+	 * on SOCs like APQ8016 the channel control bits start
+	 * at different offset to ipq806x
+	 **/
+	u32	rdmactl_audif_start;
+	/* SOC specific intialization like clocks */
+	int (*init)(struct platform_device *pdev);
+	int (*exit)(struct platform_device *pdev);
+	int (*alloc_dma_channel)(struct lpass_data *data);
+	int (*free_dma_channel)(struct lpass_data *data, int ch);
+
+	/* SOC specific dais */
+	struct snd_soc_dai_driver *dai_driver;
+	int num_dai;
 };
 };
 
 
 /* register the platform driver from the CPU DAI driver */
 /* register the platform driver from the CPU DAI driver */
 int asoc_qcom_lpass_platform_register(struct platform_device *);
 int asoc_qcom_lpass_platform_register(struct platform_device *);
+int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev);
+int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev);
+int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai);
+extern struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops;
 
 
 #endif /* __LPASS_H__ */
 #endif /* __LPASS_H__ */

+ 23 - 9
sound/soc/sh/rcar/core.c

@@ -170,6 +170,14 @@ void rsnd_mod_quit(struct rsnd_mod *mod)
 		clk_unprepare(mod->clk);
 		clk_unprepare(mod->clk);
 }
 }
 
 
+int rsnd_mod_is_working(struct rsnd_mod *mod)
+{
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+
+	/* see rsnd_dai_stream_init/quit() */
+	return !!io->substream;
+}
+
 /*
 /*
  *	settting function
  *	settting function
  */
  */
@@ -272,9 +280,10 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
 	return priv->rdai + id;
 	return priv->rdai + id;
 }
 }
 
 
+#define rsnd_dai_to_priv(dai) snd_soc_dai_get_drvdata(dai)
 static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
 static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
 {
 {
-	struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
 
 
 	return rsnd_rdai_get(priv, dai->id);
 	return rsnd_rdai_get(priv, dai->id);
 }
 }
@@ -314,7 +323,7 @@ void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
 	}
 	}
 }
 }
 
 
-static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
+static void rsnd_dai_stream_init(struct rsnd_dai_stream *io,
 				struct snd_pcm_substream *substream)
 				struct snd_pcm_substream *substream)
 {
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
@@ -326,8 +335,11 @@ static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
 				  runtime->channels *
 				  runtime->channels *
 				  samples_to_bytes(runtime, 1);
 				  samples_to_bytes(runtime, 1);
 	io->next_period_byte	= io->byte_per_period;
 	io->next_period_byte	= io->byte_per_period;
+}
 
 
-	return 0;
+static void rsnd_dai_stream_quit(struct rsnd_dai_stream *io)
+{
+	io->substream		= NULL;
 }
 }
 
 
 static
 static
@@ -351,20 +363,18 @@ struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai,
 static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 			    struct snd_soc_dai *dai)
 			    struct snd_soc_dai *dai)
 {
 {
-	struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
+	struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
 	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
 	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
 	struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
 	struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
 	int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io));
 	int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io));
 	int ret;
 	int ret;
 	unsigned long flags;
 	unsigned long flags;
 
 
-	rsnd_lock(priv, flags);
+	spin_lock_irqsave(&priv->lock, flags);
 
 
 	switch (cmd) {
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_START:
-		ret = rsnd_dai_stream_init(io, substream);
-		if (ret < 0)
-			goto dai_trigger_end;
+		rsnd_dai_stream_init(io, substream);
 
 
 		ret = rsnd_platform_call(priv, dai, start, ssi_id);
 		ret = rsnd_platform_call(priv, dai, start, ssi_id);
 		if (ret < 0)
 		if (ret < 0)
@@ -390,13 +400,15 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 		ret = rsnd_platform_call(priv, dai, stop, ssi_id);
 		ret = rsnd_platform_call(priv, dai, stop, ssi_id);
 		if (ret < 0)
 		if (ret < 0)
 			goto dai_trigger_end;
 			goto dai_trigger_end;
+
+		rsnd_dai_stream_quit(io);
 		break;
 		break;
 	default:
 	default:
 		ret = -EINVAL;
 		ret = -EINVAL;
 	}
 	}
 
 
 dai_trigger_end:
 dai_trigger_end:
-	rsnd_unlock(priv, flags);
+	spin_unlock_irqrestore(&priv->lock, flags);
 
 
 	return ret;
 	return ret;
 }
 }
@@ -833,12 +845,14 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
 			    struct rsnd_kctrl_cfg *cfg,
 			    struct rsnd_kctrl_cfg *cfg,
 			    void (*update)(struct rsnd_mod *mod))
 			    void (*update)(struct rsnd_mod *mod))
 {
 {
+	struct snd_soc_card *soc_card = rtd->card;
 	struct snd_card *card = rtd->card->snd_card;
 	struct snd_card *card = rtd->card->snd_card;
 	struct snd_kcontrol *kctrl;
 	struct snd_kcontrol *kctrl;
 	struct snd_kcontrol_new knew = {
 	struct snd_kcontrol_new knew = {
 		.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name		= name,
 		.name		= name,
 		.info		= rsnd_kctrl_info,
 		.info		= rsnd_kctrl_info,
+		.index		= rtd - soc_card->rtd,
 		.get		= rsnd_kctrl_get,
 		.get		= rsnd_kctrl_get,
 		.put		= rsnd_kctrl_put,
 		.put		= rsnd_kctrl_put,
 		.private_value	= (unsigned long)cfg,
 		.private_value	= (unsigned long)cfg,

+ 1 - 2
sound/soc/sh/rcar/rsnd.h

@@ -303,6 +303,7 @@ int rsnd_mod_init(struct rsnd_mod *mod,
 		   int id);
 		   int id);
 void rsnd_mod_quit(struct rsnd_mod *mod);
 void rsnd_mod_quit(struct rsnd_mod *mod);
 char *rsnd_mod_name(struct rsnd_mod *mod);
 char *rsnd_mod_name(struct rsnd_mod *mod);
+int rsnd_mod_is_working(struct rsnd_mod *mod);
 struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod);
 struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod);
 
 
 /*
 /*
@@ -449,8 +450,6 @@ struct rsnd_priv {
 #define rsnd_priv_to_pdev(priv)	((priv)->pdev)
 #define rsnd_priv_to_pdev(priv)	((priv)->pdev)
 #define rsnd_priv_to_dev(priv)	(&(rsnd_priv_to_pdev(priv)->dev))
 #define rsnd_priv_to_dev(priv)	(&(rsnd_priv_to_pdev(priv)->dev))
 #define rsnd_priv_to_info(priv)	((priv)->info)
 #define rsnd_priv_to_info(priv)	((priv)->info)
-#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
-#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)
 
 
 /*
 /*
  *	rsnd_kctrl
  *	rsnd_kctrl

+ 1 - 0
sound/soc/sh/rcar/rsrc-card.c

@@ -232,6 +232,7 @@ rsrc_card_sub_parse_of(struct rsrc_card_priv *priv,
 	if (args_count) {
 	if (args_count) {
 		*args_count = args.args_count;
 		*args_count = args.args_count;
 		dai_link->dynamic = 1;
 		dai_link->dynamic = 1;
+		dai_link->dpcm_merged_format = 1;
 	} else {
 	} else {
 		dai_link->no_pcm = 1;
 		dai_link->no_pcm = 1;
 		priv->codec_conf.of_node = (*p_node);
 		priv->codec_conf.of_node = (*p_node);

+ 8 - 3
sound/soc/sh/rcar/src.c

@@ -673,10 +673,13 @@ static int _rsnd_src_stop_gen2(struct rsnd_mod *mod)
 static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
 static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
 {
 {
 	struct rsnd_mod *mod = data;
 	struct rsnd_mod *mod = data;
-	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+
+	spin_lock(&priv->lock);
 
 
-	if (!io)
-		return IRQ_NONE;
+	/* ignore all cases if not working */
+	if (!rsnd_mod_is_working(mod))
+		goto rsnd_src_interrupt_gen2_out;
 
 
 	if (rsnd_src_error_record_gen2(mod)) {
 	if (rsnd_src_error_record_gen2(mod)) {
 		struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 		struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
@@ -692,6 +695,8 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
 		else
 		else
 			dev_warn(dev, "no more SRC restart\n");
 			dev_warn(dev, "no more SRC restart\n");
 	}
 	}
+rsnd_src_interrupt_gen2_out:
+	spin_unlock(&priv->lock);
 
 
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }

+ 59 - 11
sound/soc/sh/rcar/ssi.c

@@ -66,6 +66,7 @@ struct rsnd_ssi {
 
 
 	u32 cr_own;
 	u32 cr_own;
 	u32 cr_clk;
 	u32 cr_clk;
+	int chan;
 	int err;
 	int err;
 	unsigned int usrcnt;
 	unsigned int usrcnt;
 };
 };
@@ -80,7 +81,7 @@ struct rsnd_ssi {
 #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
 #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
 #define rsnd_dma_to_ssi(dma)  rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
 #define rsnd_dma_to_ssi(dma)  rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
 #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
 #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
-#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
+#define rsnd_ssi_parent(ssi) ((ssi)->parent)
 #define rsnd_ssi_mode_flags(p) ((p)->info->flags)
 #define rsnd_ssi_mode_flags(p) ((p)->info->flags)
 #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
 #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
 #define rsnd_ssi_of_node(priv) \
 #define rsnd_ssi_of_node(priv) \
@@ -189,8 +190,10 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
 		rsnd_mod_hw_start(&ssi->mod);
 		rsnd_mod_hw_start(&ssi->mod);
 
 
 		if (rsnd_rdai_is_clk_master(rdai)) {
 		if (rsnd_rdai_is_clk_master(rdai)) {
-			if (rsnd_ssi_clk_from_parent(ssi))
-				rsnd_ssi_hw_start(ssi->parent, io);
+			struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
+
+			if (ssi_parent)
+				rsnd_ssi_hw_start(ssi_parent, io);
 			else
 			else
 				rsnd_ssi_master_clk_start(ssi, io);
 				rsnd_ssi_master_clk_start(ssi, io);
 		}
 		}
@@ -229,8 +232,10 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
 	struct device *dev = rsnd_priv_to_dev(priv);
 	struct device *dev = rsnd_priv_to_dev(priv);
 	u32 cr;
 	u32 cr;
 
 
-	if (0 == ssi->usrcnt) /* stop might be called without start */
+	if (0 == ssi->usrcnt) {
+		dev_err(dev, "%s called without starting\n", __func__);
 		return;
 		return;
+	}
 
 
 	ssi->usrcnt--;
 	ssi->usrcnt--;
 
 
@@ -253,13 +258,17 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi)
 		rsnd_ssi_status_check(&ssi->mod, IIRQ);
 		rsnd_ssi_status_check(&ssi->mod, IIRQ);
 
 
 		if (rsnd_rdai_is_clk_master(rdai)) {
 		if (rsnd_rdai_is_clk_master(rdai)) {
-			if (rsnd_ssi_clk_from_parent(ssi))
-				rsnd_ssi_hw_stop(ssi->parent);
+			struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
+
+			if (ssi_parent)
+				rsnd_ssi_hw_stop(ssi_parent);
 			else
 			else
 				rsnd_ssi_master_clk_stop(ssi);
 				rsnd_ssi_master_clk_stop(ssi);
 		}
 		}
 
 
 		rsnd_mod_hw_stop(&ssi->mod);
 		rsnd_mod_hw_stop(&ssi->mod);
+
+		ssi->chan = 0;
 	}
 	}
 
 
 	dev_dbg(dev, "%s[%d] hw stopped\n",
 	dev_dbg(dev, "%s[%d] hw stopped\n",
@@ -336,6 +345,35 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
 	return 0;
 	return 0;
 }
 }
 
 
+static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
+			      struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+	struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
+	int chan = params_channels(params);
+
+	/*
+	 * Already working.
+	 * It will happen if SSI has parent/child connection.
+	 */
+	if (ssi->usrcnt) {
+		/*
+		 * it is error if child <-> parent SSI uses
+		 * different channels.
+		 */
+		if (ssi->chan != chan)
+			return -EIO;
+	}
+
+	/* It will be removed on rsnd_ssi_hw_stop */
+	ssi->chan = chan;
+	if (ssi_parent)
+		return rsnd_ssi_hw_params(&ssi_parent->mod, substream, params);
+
+	return 0;
+}
+
 static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
 static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
 {
 {
 	/* under/over flow error */
 	/* under/over flow error */
@@ -385,10 +423,15 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
 	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
 	int is_dma = rsnd_ssi_is_dma_mode(mod);
 	int is_dma = rsnd_ssi_is_dma_mode(mod);
-	u32 status = rsnd_mod_read(mod, SSISR);
+	u32 status;
+
+	spin_lock(&priv->lock);
 
 
-	if (!io)
-		return IRQ_NONE;
+	/* ignore all cases if not working */
+	if (!rsnd_mod_is_working(mod))
+		goto rsnd_ssi_interrupt_out;
+
+	status = rsnd_mod_read(mod, SSISR);
 
 
 	/* PIO only */
 	/* PIO only */
 	if (!is_dma && (status & DIRQ)) {
 	if (!is_dma && (status & DIRQ)) {
@@ -428,6 +471,9 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
 
 
 	rsnd_ssi_record_error(ssi, status);
 	rsnd_ssi_record_error(ssi, status);
 
 
+rsnd_ssi_interrupt_out:
+	spin_unlock(&priv->lock);
+
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
@@ -456,6 +502,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
 	.quit	= rsnd_ssi_quit,
 	.quit	= rsnd_ssi_quit,
 	.start	= rsnd_ssi_start,
 	.start	= rsnd_ssi_start,
 	.stop	= rsnd_ssi_stop,
 	.stop	= rsnd_ssi_stop,
+	.hw_params = rsnd_ssi_hw_params,
 };
 };
 
 
 static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
 static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
@@ -565,6 +612,7 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
 	.start	= rsnd_ssi_dma_start,
 	.start	= rsnd_ssi_dma_start,
 	.stop	= rsnd_ssi_dma_stop,
 	.stop	= rsnd_ssi_dma_stop,
 	.fallback = rsnd_ssi_fallback,
 	.fallback = rsnd_ssi_fallback,
+	.hw_params = rsnd_ssi_hw_params,
 };
 };
 
 
 int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
 int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
@@ -598,7 +646,7 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
 	return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
 	return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
 }
 }
 
 
-static void rsnd_ssi_parent_clk_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
+static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
 {
 {
 	if (!rsnd_ssi_is_pin_sharing(&ssi->mod))
 	if (!rsnd_ssi_is_pin_sharing(&ssi->mod))
 		return;
 		return;
@@ -732,7 +780,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
 
 
-		rsnd_ssi_parent_clk_setup(priv, ssi);
+		rsnd_ssi_parent_setup(priv, ssi);
 	}
 	}
 
 
 	return 0;
 	return 0;