Browse Source

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

Mark Brown 10 years ago

+ 43 - 0
Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt

@@ -0,0 +1,43 @@
+* Qualcomm Technologies LPASS CPU DAI
+
+This node models the Qualcomm Technologies Low-Power Audio SubSystem (LPASS).
+
+Required properties:
+
+- compatible		: "qcom,lpass-cpu"
+- clocks		: Must contain an entry for each entry in clock-names.
+- clock-names		: A list which must include the following entries:
+				* "ahbix-clk"
+				* "mi2s-osr-clk"
+				* "mi2s-bit-clk"
+- interrupts		: Must contain an entry for each entry in
+			  interrupt-names.
+- interrupt-names	: A list which must include the following entries:
+				* "lpass-irq-lpaif"
+- pinctrl-N		: One property must exist for each entry in
+			  pinctrl-names.  See ../pinctrl/pinctrl-bindings.txt
+			  for details of the property values.
+- pinctrl-names		: Must contain a "default" entry.
+- reg			: Must contain an address for each entry in reg-names.
+- reg-names		: A list which must include the following entries:
+				* "lpass-lpaif"
+
+Optional properties:
+
+- qcom,adsp		: Phandle for the audio DSP node
+
+Example:
+
+lpass@28100000 {
+	compatible = "qcom,lpass-cpu";
+	clocks = <&lcc AHBIX_CLK>, <&lcc MI2S_OSR_CLK>, <&lcc MI2S_BIT_CLK>;
+	clock-names = "ahbix-clk", "mi2s-osr-clk", "mi2s-bit-clk";
+	interrupts = <0 85 1>;
+	interrupt-names = "lpass-irq-lpaif";
+	pinctrl-names = "default", "idle";
+	pinctrl-0 = <&mi2s_default>;
+	pinctrl-1 = <&mi2s_idle>;
+	reg = <0x28100000 0x10000>;
+	reg-names = "lpass-lpaif";
+	qcom,adsp = <&adsp>;
+};

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

@@ -29,9 +29,17 @@ SSI subnode properties:
 - shared-pin			: if shared clock pin
 - shared-pin			: if shared clock pin
 - pio-transfer			: use PIO transfer mode
 - pio-transfer			: use PIO transfer mode
 - no-busif			: BUSIF is not ussed when [mem -> SSI] via DMA case
 - no-busif			: BUSIF is not ussed when [mem -> SSI] via DMA case
+- dma				: Should contain Audio DMAC entry
+- dma-names			: SSI  case "rx"  (=playback), "tx"  (=capture)
+				  SSIU case "rxu" (=playback), "txu" (=capture)
 
 
 SRC subnode properties:
 SRC subnode properties:
-no properties at this point
+- dma				: Should contain Audio DMAC entry
+- dma-names			: "rx" (=playback), "tx" (=capture)
+
+DVC subnode properties:
+- dma				: Should contain Audio DMAC entry
+- dma-names			: "tx" (=playback/capture)
 
 
 DAI subnode properties:
 DAI subnode properties:
 - playback			: list of playback modules
 - playback			: list of playback modules
@@ -45,56 +53,145 @@ rcar_sound: rcar_sound@ec500000 {
 	reg =	<0 0xec500000 0 0x1000>, /* SCU */
 	reg =	<0 0xec500000 0 0x1000>, /* SCU */
 		<0 0xec5a0000 0 0x100>,  /* ADG */
 		<0 0xec5a0000 0 0x100>,  /* ADG */
 		<0 0xec540000 0 0x1000>, /* SSIU */
 		<0 0xec540000 0 0x1000>, /* SSIU */
-		<0 0xec541000 0 0x1280>; /* SSI */
+		<0 0xec541000 0 0x1280>, /* SSI */
+		<0 0xec740000 0 0x200>;  /* Audio DMAC peri peri*/
+	reg-names = "scu", "adg", "ssiu", "ssi", "audmapp";
+
+	clocks = <&mstp10_clks R8A7790_CLK_SSI_ALL>,
+		<&mstp10_clks R8A7790_CLK_SSI9>, <&mstp10_clks R8A7790_CLK_SSI8>,
+		<&mstp10_clks R8A7790_CLK_SSI7>, <&mstp10_clks R8A7790_CLK_SSI6>,
+		<&mstp10_clks R8A7790_CLK_SSI5>, <&mstp10_clks R8A7790_CLK_SSI4>,
+		<&mstp10_clks R8A7790_CLK_SSI3>, <&mstp10_clks R8A7790_CLK_SSI2>,
+		<&mstp10_clks R8A7790_CLK_SSI1>, <&mstp10_clks R8A7790_CLK_SSI0>,
+		<&mstp10_clks R8A7790_CLK_SCU_SRC9>, <&mstp10_clks R8A7790_CLK_SCU_SRC8>,
+		<&mstp10_clks R8A7790_CLK_SCU_SRC7>, <&mstp10_clks R8A7790_CLK_SCU_SRC6>,
+		<&mstp10_clks R8A7790_CLK_SCU_SRC5>, <&mstp10_clks R8A7790_CLK_SCU_SRC4>,
+		<&mstp10_clks R8A7790_CLK_SCU_SRC3>, <&mstp10_clks R8A7790_CLK_SCU_SRC2>,
+		<&mstp10_clks R8A7790_CLK_SCU_SRC1>, <&mstp10_clks R8A7790_CLK_SCU_SRC0>,
+		<&mstp10_clks R8A7790_CLK_SCU_DVC0>, <&mstp10_clks R8A7790_CLK_SCU_DVC1>,
+		<&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, <&m2_clk>;
+	clock-names = "ssi-all",
+			"ssi.9", "ssi.8", "ssi.7", "ssi.6", "ssi.5",
+			"ssi.4", "ssi.3", "ssi.2", "ssi.1", "ssi.0",
+			"src.9", "src.8", "src.7", "src.6", "src.5",
+			"src.4", "src.3", "src.2", "src.1", "src.0",
+			"dvc.0", "dvc.1",
+			"clk_a", "clk_b", "clk_c", "clk_i";
 
 
 	rcar_sound,dvc {
 	rcar_sound,dvc {
-		dvc0: dvc@0 { };
-		dvc1: dvc@1 { };
+		dvc0: dvc@0 {
+			dmas = <&audma0 0xbc>;
+			dma-names = "tx";
+		};
+		dvc1: dvc@1 {
+			dmas = <&audma0 0xbe>;
+			dma-names = "tx";
+		};
 	};
 	};
 
 
 	rcar_sound,src {
 	rcar_sound,src {
-		src0: src@0 { };
-		src1: src@1 { };
-		src2: src@2 { };
-		src3: src@3 { };
-		src4: src@4 { };
-		src5: src@5 { };
-		src6: src@6 { };
-		src7: src@7 { };
-		src8: src@8 { };
-		src9: src@9 { };
+		src0: src@0 {
+			interrupts = <0 352 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x85>, <&audma1 0x9a>;
+			dma-names = "rx", "tx";
+		};
+		src1: src@1 {
+			interrupts = <0 353 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x87>, <&audma1 0x9c>;
+			dma-names = "rx", "tx";
+		};
+		src2: src@2 {
+			interrupts = <0 354 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x89>, <&audma1 0x9e>;
+			dma-names = "rx", "tx";
+		};
+		src3: src@3 {
+			interrupts = <0 355 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x8b>, <&audma1 0xa0>;
+			dma-names = "rx", "tx";
+		};
+		src4: src@4 {
+			interrupts = <0 356 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x8d>, <&audma1 0xb0>;
+			dma-names = "rx", "tx";
+		};
+		src5: src@5 {
+			interrupts = <0 357 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x8f>, <&audma1 0xb2>;
+			dma-names = "rx", "tx";
+		};
+		src6: src@6 {
+			interrupts = <0 358 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x91>, <&audma1 0xb4>;
+			dma-names = "rx", "tx";
+		};
+		src7: src@7 {
+			interrupts = <0 359 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x93>, <&audma1 0xb6>;
+			dma-names = "rx", "tx";
+		};
+		src8: src@8 {
+			interrupts = <0 360 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x95>, <&audma1 0xb8>;
+			dma-names = "rx", "tx";
+		};
+		src9: src@9 {
+			interrupts = <0 361 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x97>, <&audma1 0xba>;
+			dma-names = "rx", "tx";
+		};
 	};
 	};
 
 
 	rcar_sound,ssi {
 	rcar_sound,ssi {
 		ssi0: ssi@0 {
 		ssi0: ssi@0 {
 			interrupts = <0 370 IRQ_TYPE_LEVEL_HIGH>;
 			interrupts = <0 370 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x01>, <&audma1 0x02>, <&audma0 0x15>, <&audma1 0x16>;
+			dma-names = "rx", "tx", "rxu", "txu";
 		};
 		};
 		ssi1: ssi@1 {
 		ssi1: ssi@1 {
 			interrupts = <0 371 IRQ_TYPE_LEVEL_HIGH>;
 			interrupts = <0 371 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x03>, <&audma1 0x04>, <&audma0 0x49>, <&audma1 0x4a>;
+			dma-names = "rx", "tx", "rxu", "txu";
 		};
 		};
 		ssi2: ssi@2 {
 		ssi2: ssi@2 {
 			interrupts = <0 372 IRQ_TYPE_LEVEL_HIGH>;
 			interrupts = <0 372 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x05>, <&audma1 0x06>, <&audma0 0x63>, <&audma1 0x64>;
+			dma-names = "rx", "tx", "rxu", "txu";
 		};
 		};
 		ssi3: ssi@3 {
 		ssi3: ssi@3 {
 			interrupts = <0 373 IRQ_TYPE_LEVEL_HIGH>;
 			interrupts = <0 373 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x07>, <&audma1 0x08>, <&audma0 0x6f>, <&audma1 0x70>;
+			dma-names = "rx", "tx", "rxu", "txu";
 		};
 		};
 		ssi4: ssi@4 {
 		ssi4: ssi@4 {
 			interrupts = <0 374 IRQ_TYPE_LEVEL_HIGH>;
 			interrupts = <0 374 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x09>, <&audma1 0x0a>, <&audma0 0x71>, <&audma1 0x72>;
+			dma-names = "rx", "tx", "rxu", "txu";
 		};
 		};
 		ssi5: ssi@5 {
 		ssi5: ssi@5 {
 			interrupts = <0 375 IRQ_TYPE_LEVEL_HIGH>;
 			interrupts = <0 375 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x0b>, <&audma1 0x0c>, <&audma0 0x73>, <&audma1 0x74>;
+			dma-names = "rx", "tx", "rxu", "txu";
 		};
 		};
 		ssi6: ssi@6 {
 		ssi6: ssi@6 {
 			interrupts = <0 376 IRQ_TYPE_LEVEL_HIGH>;
 			interrupts = <0 376 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x0d>, <&audma1 0x0e>, <&audma0 0x75>, <&audma1 0x76>;
+			dma-names = "rx", "tx", "rxu", "txu";
 		};
 		};
 		ssi7: ssi@7 {
 		ssi7: ssi@7 {
 			interrupts = <0 377 IRQ_TYPE_LEVEL_HIGH>;
 			interrupts = <0 377 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x0f>, <&audma1 0x10>, <&audma0 0x79>, <&audma1 0x7a>;
+			dma-names = "rx", "tx", "rxu", "txu";
 		};
 		};
 		ssi8: ssi@8 {
 		ssi8: ssi@8 {
 			interrupts = <0 378 IRQ_TYPE_LEVEL_HIGH>;
 			interrupts = <0 378 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x11>, <&audma1 0x12>, <&audma0 0x7b>, <&audma1 0x7c>;
+			dma-names = "rx", "tx", "rxu", "txu";
 		};
 		};
 		ssi9: ssi@9 {
 		ssi9: ssi@9 {
 			interrupts = <0 379 IRQ_TYPE_LEVEL_HIGH>;
 			interrupts = <0 379 IRQ_TYPE_LEVEL_HIGH>;
+			dmas = <&audma0 0x13>, <&audma1 0x14>, <&audma0 0x7d>, <&audma1 0x7e>;
+			dma-names = "rx", "tx", "rxu", "txu";
 		};
 		};
 	};
 	};
 
 

+ 67 - 0
Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt

@@ -0,0 +1,67 @@
+Renesas Sampling Rate Convert Sound Card:
+
+Renesas Sampling Rate Convert Sound Card specifies audio DAI connections of SoC <-> codec.
+
+Required properties:
+
+- compatible				: "renesas,rsrc-card,<board>"
+					  Examples with soctypes are:
+					    - "renesas,rsrc-card,lager"
+					    - "renesas,rsrc-card,koelsch"
+Optional properties:
+
+- card_name				: User specified audio sound card name, one string
+					  property.
+- cpu					: CPU   sub-node
+- codec					: CODEC sub-node
+
+Optional subnode properties:
+
+- format				: CPU/CODEC common audio format.
+					  "i2s", "right_j", "left_j" , "dsp_a"
+					  "dsp_b", "ac97", "pdm", "msb", "lsb"
+- frame-master				: Indicates dai-link frame master.
+					  phandle to a cpu or codec subnode.
+- bitclock-master			: Indicates dai-link bit clock master.
+					  phandle to a cpu or codec subnode.
+- bitclock-inversion			: bool property. Add this if the
+					  dai-link uses bit clock inversion.
+- frame-inversion			: bool property. Add this if the
+					  dai-link uses frame clock inversion.
+- convert-rate				: platform specified sampling rate convert
+
+Required CPU/CODEC subnodes properties:
+
+- sound-dai				: phandle and port of CPU/CODEC
+
+Optional CPU/CODEC subnodes properties:
+
+- clocks / system-clock-frequency	: specify subnode's clock if needed.
+					  it can be specified via "clocks" if system has
+					  clock node (= common clock), or "system-clock-frequency"
+					  (if system doens't support common clock)
+					  If a clock is specified, it is
+					  enabled with clk_prepare_enable()
+					  in dai startup() and disabled with
+					  clk_disable_unprepare() in dai
+					  shutdown().
+
+Example
+
+sound {
+	compatible = "renesas,rsrc-card,lager";
+
+	card-name = "rsnd-ak4643";
+	format = "left_j";
+	bitclock-master = <&sndcodec>;
+	frame-master = <&sndcodec>;
+
+	sndcpu: cpu {
+		sound-dai = <&rcar_sound>;
+	};
+
+	sndcodec: codec {
+		sound-dai = <&ak4643>;
+		system-clock-frequency = <11289600>;
+	};
+};

+ 23 - 0
Documentation/devicetree/bindings/sound/storm.txt

@@ -0,0 +1,23 @@
+* Sound complex for Storm boards
+
+Models a soundcard for Storm boards with the Qualcomm Technologies IPQ806x SOC
+connected to a MAX98357A DAC via I2S.
+
+Required properties:
+
+- compatible	: "google,storm-audio"
+- cpu		: Phandle of the CPU DAI
+- codec		: Phandle of the codec DAI
+
+Optional properties:
+
+- qcom,model	: The user-visible name of this sound card.
+
+Example:
+
+sound {
+	compatible = "google,storm-audio";
+	qcom,model = "ipq806x-storm";
+	cpu = <&lpass_cpu>;
+	codec = <&max98357a>;
+};

+ 7 - 0
MAINTAINERS

@@ -5271,6 +5271,13 @@ F:	drivers/char/ipmi/
 F:	include/linux/ipmi*
 F:	include/linux/ipmi*
 F:	include/uapi/linux/ipmi*
 F:	include/uapi/linux/ipmi*
 
 
+QCOM AUDIO (ASoC) DRIVERS
+M:	Patrick Lai <plai@codeaurora.org>
+M:	Banajit Goswami <bgoswami@codeaurora.org>
+L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
+S:	Supported
+F:	sound/soc/qcom/
+
 IPS SCSI RAID DRIVER
 IPS SCSI RAID DRIVER
 M:	Adaptec OEM Raid Solutions <aacraid@adaptec.com>
 M:	Adaptec OEM Raid Solutions <aacraid@adaptec.com>
 L:	linux-scsi@vger.kernel.org
 L:	linux-scsi@vger.kernel.org

+ 1 - 0
sound/soc/Kconfig

@@ -47,6 +47,7 @@ source "sound/soc/kirkwood/Kconfig"
 source "sound/soc/intel/Kconfig"
 source "sound/soc/intel/Kconfig"
 source "sound/soc/mxs/Kconfig"
 source "sound/soc/mxs/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/pxa/Kconfig"
+source "sound/soc/qcom/Kconfig"
 source "sound/soc/rockchip/Kconfig"
 source "sound/soc/rockchip/Kconfig"
 source "sound/soc/samsung/Kconfig"
 source "sound/soc/samsung/Kconfig"
 source "sound/soc/sh/Kconfig"
 source "sound/soc/sh/Kconfig"

+ 1 - 0
sound/soc/Makefile

@@ -28,6 +28,7 @@ obj-$(CONFIG_SND_SOC)	+= nuc900/
 obj-$(CONFIG_SND_SOC)	+= omap/
 obj-$(CONFIG_SND_SOC)	+= omap/
 obj-$(CONFIG_SND_SOC)	+= kirkwood/
 obj-$(CONFIG_SND_SOC)	+= kirkwood/
 obj-$(CONFIG_SND_SOC)	+= pxa/
 obj-$(CONFIG_SND_SOC)	+= pxa/
+obj-$(CONFIG_SND_SOC)	+= qcom/
 obj-$(CONFIG_SND_SOC)	+= rockchip/
 obj-$(CONFIG_SND_SOC)	+= rockchip/
 obj-$(CONFIG_SND_SOC)	+= samsung/
 obj-$(CONFIG_SND_SOC)	+= samsung/
 obj-$(CONFIG_SND_SOC)	+= sh/
 obj-$(CONFIG_SND_SOC)	+= sh/

+ 18 - 7
sound/soc/codecs/rt286.c

@@ -397,7 +397,7 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 
 
 	if (jack) {
 	if (jack) {
 		/* enable IRQ */
 		/* enable IRQ */
-		if (rt286->jack->status | SND_JACK_HEADPHONE)
+		if (rt286->jack->status & SND_JACK_HEADPHONE)
 			snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO1");
 			snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO1");
 		regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2);
 		regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2);
 		/* Send an initial empty report */
 		/* Send an initial empty report */
@@ -1048,7 +1048,6 @@ static int rt286_probe(struct snd_soc_codec *codec)
 	struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
 	struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
 
 
 	rt286->codec = codec;
 	rt286->codec = codec;
-	codec->dapm.bias_level = SND_SOC_BIAS_OFF;
 
 
 	if (rt286->i2c->irq) {
 	if (rt286->i2c->irq) {
 		regmap_update_bits(rt286->regmap,
 		regmap_update_bits(rt286->regmap,
@@ -1220,7 +1219,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
 {
 {
 	struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev);
 	struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev);
 	struct rt286_priv *rt286;
 	struct rt286_priv *rt286;
-	int i, ret;
+	int i, ret, val;
 
 
 	rt286 = devm_kzalloc(&i2c->dev,	sizeof(*rt286),
 	rt286 = devm_kzalloc(&i2c->dev,	sizeof(*rt286),
 				GFP_KERNEL);
 				GFP_KERNEL);
@@ -1235,11 +1234,15 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
 		return ret;
 		return ret;
 	}
 	}
 
 
-	regmap_read(rt286->regmap,
-		RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret);
-	if (ret != RT286_VENDOR_ID && ret != RT288_VENDOR_ID) {
+	ret = regmap_read(rt286->regmap,
+		RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "I2C error %d\n", ret);
+		return ret;
+	}
+	if (val != RT286_VENDOR_ID && val != RT288_VENDOR_ID) {
 		dev_err(&i2c->dev,
 		dev_err(&i2c->dev,
-			"Device with ID register %x is not rt286\n", ret);
+			"Device with ID register %x is not rt286\n", val);
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 
 
@@ -1247,6 +1250,14 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
 	rt286->i2c = i2c;
 	rt286->i2c = i2c;
 	i2c_set_clientdata(i2c, rt286);
 	i2c_set_clientdata(i2c, rt286);
 
 
+	/* restore codec default */
+	for (i = 0; i < INDEX_CACHE_SIZE; i++)
+		regmap_write(rt286->regmap, rt286->index_cache[i].reg,
+				rt286->index_cache[i].def);
+	for (i = 0; i < ARRAY_SIZE(rt286_reg); i++)
+		regmap_write(rt286->regmap, rt286_reg[i].reg,
+				rt286_reg[i].def);
+
 	if (pdata)
 	if (pdata)
 		rt286->pdata = *pdata;
 		rt286->pdata = *pdata;
 
 

+ 1 - 1
sound/soc/omap/Kconfig

@@ -105,7 +105,7 @@ config SND_OMAP_SOC_OMAP_ABE_TWL6040
 	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 SOC_OMAP5
+	select COMMON_CLK_PALMAS if 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:

+ 2 - 21
sound/soc/omap/n810.c

@@ -98,12 +98,11 @@ static int n810_startup(struct snd_pcm_substream *substream)
 {
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->codec;
 
 
 	snd_pcm_hw_constraint_minmax(runtime,
 	snd_pcm_hw_constraint_minmax(runtime,
 				     SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
 				     SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
 
 
-	n810_ext_control(&codec->dapm);
+	n810_ext_control(&rtd->card->dapm);
 	return clk_prepare_enable(sys_clkout2);
 	return clk_prepare_enable(sys_clkout2);
 }
 }
 
 
@@ -255,24 +254,6 @@ static const struct snd_kcontrol_new aic33_n810_controls[] = {
 		     n810_get_input, n810_set_input),
 		     n810_get_input, n810_set_input),
 };
 };
 
 
-static int n810_aic33_init(struct snd_soc_pcm_runtime *rtd)
-{
-	struct snd_soc_codec *codec = rtd->codec;
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-	/* Not connected */
-	snd_soc_dapm_nc_pin(dapm, "MONO_LOUT");
-	snd_soc_dapm_nc_pin(dapm, "HPLCOM");
-	snd_soc_dapm_nc_pin(dapm, "HPRCOM");
-	snd_soc_dapm_nc_pin(dapm, "MIC3L");
-	snd_soc_dapm_nc_pin(dapm, "MIC3R");
-	snd_soc_dapm_nc_pin(dapm, "LINE1R");
-	snd_soc_dapm_nc_pin(dapm, "LINE2L");
-	snd_soc_dapm_nc_pin(dapm, "LINE2R");
-
-	return 0;
-}
-
 /* Digital audio interface glue - connects codec <--> CPU */
 /* Digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link n810_dai = {
 static struct snd_soc_dai_link n810_dai = {
 	.name = "TLV320AIC33",
 	.name = "TLV320AIC33",
@@ -283,7 +264,6 @@ static struct snd_soc_dai_link n810_dai = {
 	.codec_dai_name = "tlv320aic3x-hifi",
 	.codec_dai_name = "tlv320aic3x-hifi",
 	.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_CBM_CFM,
 		   SND_SOC_DAIFMT_CBM_CFM,
-	.init = n810_aic33_init,
 	.ops = &n810_ops,
 	.ops = &n810_ops,
 };
 };
 
 
@@ -300,6 +280,7 @@ static struct snd_soc_card snd_soc_n810 = {
 	.num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets),
 	.num_dapm_widgets = ARRAY_SIZE(aic33_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 struct platform_device *n810_snd_device;
 static struct platform_device *n810_snd_device;

+ 0 - 2
sound/soc/omap/omap-hdmi-audio.c

@@ -142,8 +142,6 @@ static int hdmi_dai_hw_params(struct snd_pcm_substream *substream,
 
 
 	iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE;
 	iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE;
 
 
-	iec->status[0] |= IEC958_AES1_PRO_MODE_NOTID;
-
 	iec->status[1] = IEC958_AES1_CON_GENERAL;
 	iec->status[1] = IEC958_AES1_CON_GENERAL;
 
 
 	iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC;
 	iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC;

+ 25 - 0
sound/soc/qcom/Kconfig

@@ -0,0 +1,25 @@
+config SND_SOC_QCOM
+	tristate "ASoC support for QCOM platforms"
+	help
+          Say Y or M if you want to add support to use audio devices
+          in Qualcomm Technologies SOC-based platforms.
+
+config SND_SOC_LPASS_CPU
+	tristate
+	depends on SND_SOC_QCOM
+	select REGMAP_MMIO
+
+config SND_SOC_LPASS_PLATFORM
+	tristate
+	depends on SND_SOC_QCOM
+	select REGMAP_MMIO
+
+config SND_SOC_STORM
+	tristate "ASoC I2S support for Storm boards"
+	depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST
+	select SND_SOC_LPASS_CPU
+	select SND_SOC_LPASS_PLATFORM
+	select SND_SOC_MAX98357A
+	help
+          Say Y or M if you want add support for SoC audio on the
+          Qualcomm Technologies IPQ806X-based Storm board.

+ 11 - 0
sound/soc/qcom/Makefile

@@ -0,0 +1,11 @@
+# Platform
+snd-soc-lpass-cpu-objs := lpass-cpu.o
+snd-soc-lpass-platform-objs := lpass-platform.o
+
+obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
+obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
+
+# Machine
+snd-soc-storm-objs := storm.o
+
+obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o

+ 491 - 0
sound/soc/qcom/lpass-cpu.c

@@ -0,0 +1,491 @@
+/*
+ * 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-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS
+ */
+
+#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/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include "lpass-lpaif-ipq806x.h"
+#include "lpass.h"
+
+static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+		unsigned int freq, int dir)
+{
+	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	int ret;
+
+	ret = clk_set_rate(drvdata->mi2s_osr_clk, freq);
+	if (ret)
+		dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n",
+				__func__, freq, ret);
+
+	return ret;
+}
+
+static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	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;
+	}
+
+	ret = clk_prepare_enable(drvdata->mi2s_bit_clk);
+	if (ret) {
+		dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n",
+				__func__, ret);
+		clk_disable_unprepare(drvdata->mi2s_osr_clk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *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);
+}
+
+static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	snd_pcm_format_t format = params_format(params);
+	unsigned int channels = params_channels(params);
+	unsigned int rate = params_rate(params);
+	unsigned int regval;
+	int bitwidth, ret;
+
+	bitwidth = snd_pcm_format_width(format);
+	if (bitwidth < 0) {
+		dev_err(dai->dev, "%s() invalid bit width given: %d\n",
+				__func__, bitwidth);
+		return bitwidth;
+	}
+
+	regval = LPAIF_I2SCTL_LOOPBACK_DISABLE |
+			LPAIF_I2SCTL_WSSRC_INTERNAL;
+
+	switch (bitwidth) {
+	case 16:
+		regval |= LPAIF_I2SCTL_BITWIDTH_16;
+		break;
+	case 24:
+		regval |= LPAIF_I2SCTL_BITWIDTH_24;
+		break;
+	case 32:
+		regval |= LPAIF_I2SCTL_BITWIDTH_32;
+		break;
+	default:
+		dev_err(dai->dev, "%s() invalid bitwidth given: %d\n",
+				__func__, bitwidth);
+		return -EINVAL;
+	}
+
+	switch (channels) {
+	case 1:
+		regval |= LPAIF_I2SCTL_SPKMODE_SD0;
+		regval |= LPAIF_I2SCTL_SPKMONO_MONO;
+		break;
+	case 2:
+		regval |= LPAIF_I2SCTL_SPKMODE_SD0;
+		regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+		break;
+	case 4:
+		regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
+		regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+		break;
+	case 6:
+		regval |= LPAIF_I2SCTL_SPKMODE_6CH;
+		regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+		break;
+	case 8:
+		regval |= LPAIF_I2SCTL_SPKMODE_8CH;
+		regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+		break;
+	default:
+		dev_err(dai->dev, "%s() invalid channels given: %u\n",
+				__func__, channels);
+		return -EINVAL;
+	}
+
+	ret = regmap_write(drvdata->lpaif_map,
+			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), regval);
+	if (ret) {
+		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = clk_set_rate(drvdata->mi2s_bit_clk, rate * bitwidth * 2);
+	if (ret) {
+		dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n",
+				__func__, rate * bitwidth * 2, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	int ret;
+
+	ret = regmap_write(drvdata->lpaif_map,
+			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
+	if (ret)
+		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
+				__func__, ret);
+
+	return ret;
+}
+
+static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	int ret;
+
+	ret = regmap_update_bits(drvdata->lpaif_map,
+			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+			LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
+	if (ret)
+		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
+				__func__, ret);
+
+	return ret;
+}
+
+static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
+		int cmd, struct snd_soc_dai *dai)
+{
+	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	int ret;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ret = regmap_update_bits(drvdata->lpaif_map,
+				LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+				LPAIF_I2SCTL_SPKEN_MASK,
+				LPAIF_I2SCTL_SPKEN_ENABLE);
+		if (ret)
+			dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
+					__func__, ret);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ret = regmap_update_bits(drvdata->lpaif_map,
+				LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+				LPAIF_I2SCTL_SPKEN_MASK,
+				LPAIF_I2SCTL_SPKEN_DISABLE);
+		if (ret)
+			dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
+					__func__, ret);
+		break;
+	}
+
+	return ret;
+}
+
+static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
+	.set_sysclk	= lpass_cpu_daiops_set_sysclk,
+	.startup	= lpass_cpu_daiops_startup,
+	.shutdown	= lpass_cpu_daiops_shutdown,
+	.hw_params	= lpass_cpu_daiops_hw_params,
+	.hw_free	= lpass_cpu_daiops_hw_free,
+	.prepare	= lpass_cpu_daiops_prepare,
+	.trigger	= lpass_cpu_daiops_trigger,
+};
+
+static int lpass_cpu_dai_probe(struct snd_soc_dai *dai)
+{
+	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+	int ret;
+
+	/* ensure audio hardware is disabled */
+	ret = regmap_write(drvdata->lpaif_map,
+			LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
+	if (ret)
+		dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
+				__func__, 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,
+};
+
+static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
+	.name = "lpass-cpu",
+};
+
+static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
+{
+	int i;
+
+	for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
+		if (reg == LPAIF_I2SCTL_REG(i))
+			return true;
+
+	for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
+		if (reg == LPAIF_IRQEN_REG(i))
+			return true;
+		if (reg == LPAIF_IRQCLEAR_REG(i))
+			return true;
+	}
+
+	for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
+		if (reg == LPAIF_RDMACTL_REG(i))
+			return true;
+		if (reg == LPAIF_RDMABASE_REG(i))
+			return true;
+		if (reg == LPAIF_RDMABUFF_REG(i))
+			return true;
+		if (reg == LPAIF_RDMAPER_REG(i))
+			return true;
+	}
+
+	return false;
+}
+
+static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
+{
+	int i;
+
+	for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
+		if (reg == LPAIF_I2SCTL_REG(i))
+			return true;
+
+	for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
+		if (reg == LPAIF_IRQEN_REG(i))
+			return true;
+		if (reg == LPAIF_IRQSTAT_REG(i))
+			return true;
+	}
+
+	for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
+		if (reg == LPAIF_RDMACTL_REG(i))
+			return true;
+		if (reg == LPAIF_RDMABASE_REG(i))
+			return true;
+		if (reg == LPAIF_RDMABUFF_REG(i))
+			return true;
+		if (reg == LPAIF_RDMACURR_REG(i))
+			return true;
+		if (reg == LPAIF_RDMAPER_REG(i))
+			return true;
+	}
+
+	return false;
+}
+
+static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
+{
+	int i;
+
+	for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i)
+		if (reg == LPAIF_IRQSTAT_REG(i))
+			return true;
+
+	for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i)
+		if (reg == LPAIF_RDMACURR_REG(i))
+			return true;
+
+	return false;
+}
+
+static const struct regmap_config lpass_cpu_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX),
+	.writeable_reg = lpass_cpu_regmap_writeable,
+	.readable_reg = lpass_cpu_regmap_readable,
+	.volatile_reg = lpass_cpu_regmap_volatile,
+	.cache_type = REGCACHE_FLAT,
+};
+
+static int lpass_cpu_platform_probe(struct platform_device *pdev)
+{
+	struct lpass_data *drvdata;
+	struct device_node *dsp_of_node;
+	struct resource *res;
+	int ret;
+
+	dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
+	if (dsp_of_node) {
+		dev_err(&pdev->dev, "%s() DSP exists and holds audio resources\n",
+				__func__);
+		return -EBUSY;
+	}
+
+	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct lpass_data),
+			GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, drvdata);
+
+	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);
+	if (IS_ERR((void const __force *)drvdata->lpaif)) {
+		dev_err(&pdev->dev, "%s() error mapping reg resource: %ld\n",
+				__func__,
+				PTR_ERR((void const __force *)drvdata->lpaif));
+		return PTR_ERR((void const __force *)drvdata->lpaif);
+	}
+
+	drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
+			&lpass_cpu_regmap_config);
+	if (IS_ERR(drvdata->lpaif_map)) {
+		dev_err(&pdev->dev, "%s() error initializing regmap: %ld\n",
+				__func__, 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);
+	}
+
+	drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");
+	if (IS_ERR(drvdata->ahbix_clk)) {
+		dev_err(&pdev->dev, "%s() error getting ahbix-clk: %ld\n",
+				__func__, PTR_ERR(drvdata->ahbix_clk));
+		return PTR_ERR(drvdata->ahbix_clk);
+	}
+
+	ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() error setting rate on ahbix_clk: %d\n",
+				__func__, ret);
+		return ret;
+	}
+	dev_dbg(&pdev->dev, "%s() set ahbix_clk rate to %lu\n", __func__,
+			clk_get_rate(drvdata->ahbix_clk));
+
+	ret = clk_prepare_enable(drvdata->ahbix_clk);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() error enabling ahbix_clk: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = devm_snd_soc_register_component(&pdev->dev,
+			&lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n",
+				__func__, ret);
+		goto err_clk;
+	}
+
+	ret = asoc_qcom_lpass_platform_register(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() error registering platform driver: %d\n",
+				__func__, ret);
+		goto err_clk;
+	}
+
+	return 0;
+
+err_clk:
+	clk_disable_unprepare(drvdata->ahbix_clk);
+	return ret;
+}
+
+static int lpass_cpu_platform_remove(struct platform_device *pdev)
+{
+	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(drvdata->ahbix_clk);
+
+	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");

+ 172 - 0
sound/soc/qcom/lpass-lpaif-ipq806x.h

@@ -0,0 +1,172 @@
+/*
+ * 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-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
+
+/* 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_LOOPBACK_MASK	0x8000
+#define LPAIF_I2SCTL_LOOPBACK_SHIFT	15
+#define LPAIF_I2SCTL_LOOPBACK_DISABLE	(0 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
+#define LPAIF_I2SCTL_LOOPBACK_ENABLE	(1 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
+
+#define LPAIF_I2SCTL_SPKEN_MASK		0x4000
+#define LPAIF_I2SCTL_SPKEN_SHIFT	14
+#define LPAIF_I2SCTL_SPKEN_DISABLE	(0 << LPAIF_I2SCTL_SPKEN_SHIFT)
+#define LPAIF_I2SCTL_SPKEN_ENABLE	(1 << LPAIF_I2SCTL_SPKEN_SHIFT)
+
+#define LPAIF_I2SCTL_SPKMODE_MASK	0x3C00
+#define LPAIF_I2SCTL_SPKMODE_SHIFT	10
+#define LPAIF_I2SCTL_SPKMODE_NONE	(0 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_SD0	(1 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_SD1	(2 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_SD2	(3 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_SD3	(4 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_QUAD01	(5 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_QUAD23	(6 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_6CH	(7 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_8CH	(8 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+
+#define LPAIF_I2SCTL_SPKMONO_MASK	0x0200
+#define LPAIF_I2SCTL_SPKMONO_SHIFT	9
+#define LPAIF_I2SCTL_SPKMONO_STEREO	(0 << LPAIF_I2SCTL_SPKMONO_SHIFT)
+#define LPAIF_I2SCTL_SPKMONO_MONO	(1 << LPAIF_I2SCTL_SPKMONO_SHIFT)
+
+#define LPAIF_I2SCTL_WSSRC_MASK		0x0004
+#define LPAIF_I2SCTL_WSSRC_SHIFT	2
+#define LPAIF_I2SCTL_WSSRC_INTERNAL	(0 << LPAIF_I2SCTL_WSSRC_SHIFT)
+#define LPAIF_I2SCTL_WSSRC_EXTERNAL	(1 << LPAIF_I2SCTL_WSSRC_SHIFT)
+
+#define LPAIF_I2SCTL_BITWIDTH_MASK	0x0003
+#define LPAIF_I2SCTL_BITWIDTH_SHIFT	0
+#define LPAIF_I2SCTL_BITWIDTH_16	(0 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
+#define LPAIF_I2SCTL_BITWIDTH_24	(1 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
+#define LPAIF_I2SCTL_BITWIDTH_32	(2 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
+
+/* LPAIF IRQ */
+
+#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,
+
+	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_IRQ_BITSTRIDE		3
+#define LPAIF_IRQ_PER(chan)		(1 << (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_ALL(chan)		(7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
+
+/* 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,
+
+	LPAIF_RDMA_CHAN_MAX		= 4,
+	LPAIF_RDMA_CHAN_NUM		= 5,
+};
+
+#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_BURSTEN_MASK	0x800
+#define LPAIF_RDMACTL_BURSTEN_SHIFT	11
+#define LPAIF_RDMACTL_BURSTEN_SINGLE	(0 << LPAIF_RDMACTL_BURSTEN_SHIFT)
+#define LPAIF_RDMACTL_BURSTEN_INCR4	(1 << LPAIF_RDMACTL_BURSTEN_SHIFT)
+
+#define LPAIF_RDMACTL_WPSCNT_MASK	0x700
+#define LPAIF_RDMACTL_WPSCNT_SHIFT	8
+#define LPAIF_RDMACTL_WPSCNT_ONE	(0 << LPAIF_RDMACTL_WPSCNT_SHIFT)
+#define LPAIF_RDMACTL_WPSCNT_TWO	(1 << LPAIF_RDMACTL_WPSCNT_SHIFT)
+#define LPAIF_RDMACTL_WPSCNT_THREE	(2 << LPAIF_RDMACTL_WPSCNT_SHIFT)
+#define LPAIF_RDMACTL_WPSCNT_FOUR	(3 << LPAIF_RDMACTL_WPSCNT_SHIFT)
+#define LPAIF_RDMACTL_WPSCNT_SIX	(5 << LPAIF_RDMACTL_WPSCNT_SHIFT)
+#define LPAIF_RDMACTL_WPSCNT_EIGHT	(7 << LPAIF_RDMACTL_WPSCNT_SHIFT)
+
+#define LPAIF_RDMACTL_AUDINTF_MASK	0x0F0
+#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_SHIFT	1
+#define LPAIF_RDMACTL_FIFOWM_1		(0 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_2		(1 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_3		(2 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_4		(3 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_5		(4 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_6		(5 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_7		(6 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_8		(7 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+
+#define LPAIF_RDMACTL_ENABLE_MASK	0x1
+#define LPAIF_RDMACTL_ENABLE_SHIFT	0
+#define LPAIF_RDMACTL_ENABLE_OFF	(0 << LPAIF_RDMACTL_ENABLE_SHIFT)
+#define LPAIF_RDMACTL_ENABLE_ON		(1 << LPAIF_RDMACTL_ENABLE_SHIFT)
+
+#endif /* __LPASS_LPAIF_H__ */

+ 526 - 0
sound/soc/qcom/lpass-platform.c

@@ -0,0 +1,526 @@
+/*
+ * 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-platform.c -- ALSA SoC platform driver for QTi LPASS
+ */
+
+#include <linux/compiler.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <sound/memalloc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "lpass-lpaif-ipq806x.h"
+#include "lpass.h"
+
+#define LPASS_PLATFORM_BUFFER_SIZE	(16 * 1024)
+#define LPASS_PLATFORM_PERIODS		2
+
+static struct snd_pcm_hardware lpass_platform_pcm_hardware = {
+	.info			=	SNDRV_PCM_INFO_MMAP |
+					SNDRV_PCM_INFO_MMAP_VALID |
+					SNDRV_PCM_INFO_INTERLEAVED |
+					SNDRV_PCM_INFO_PAUSE |
+					SNDRV_PCM_INFO_RESUME,
+	.formats		=	SNDRV_PCM_FMTBIT_S16 |
+					SNDRV_PCM_FMTBIT_S24 |
+					SNDRV_PCM_FMTBIT_S32,
+	.rates			=	SNDRV_PCM_RATE_8000_192000,
+	.rate_min		=	8000,
+	.rate_max		=	192000,
+	.channels_min		=	1,
+	.channels_max		=	8,
+	.buffer_bytes_max	=	LPASS_PLATFORM_BUFFER_SIZE,
+	.period_bytes_max	=	LPASS_PLATFORM_BUFFER_SIZE /
+						LPASS_PLATFORM_PERIODS,
+	.period_bytes_min	=	LPASS_PLATFORM_BUFFER_SIZE /
+						LPASS_PLATFORM_PERIODS,
+	.periods_min		=	LPASS_PLATFORM_PERIODS,
+	.periods_max		=	LPASS_PLATFORM_PERIODS,
+	.fifo_size		=	0,
+};
+
+static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	int ret;
+
+	snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
+
+	runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+			SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0) {
+		dev_err(soc_runtime->dev, "%s() setting constraints failed: %d\n",
+				__func__, ret);
+		return -EINVAL;
+	}
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	return 0;
+}
+
+static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_data *drvdata =
+		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	snd_pcm_format_t format = params_format(params);
+	unsigned int channels = params_channels(params);
+	unsigned int regval;
+	int bitwidth;
+	int ret;
+
+	bitwidth = snd_pcm_format_width(format);
+	if (bitwidth < 0) {
+		dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n",
+				__func__, bitwidth);
+		return bitwidth;
+	}
+
+	regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
+			LPAIF_RDMACTL_AUDINTF_MI2S |
+			LPAIF_RDMACTL_FIFOWM_8;
+
+	switch (bitwidth) {
+	case 16:
+		switch (channels) {
+		case 1:
+		case 2:
+			regval |= LPAIF_RDMACTL_WPSCNT_ONE;
+			break;
+		case 4:
+			regval |= LPAIF_RDMACTL_WPSCNT_TWO;
+			break;
+		case 6:
+			regval |= LPAIF_RDMACTL_WPSCNT_THREE;
+			break;
+		case 8:
+			regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
+			break;
+		default:
+			dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
+					__func__, bitwidth, channels);
+			return -EINVAL;
+		}
+		break;
+	case 24:
+	case 32:
+		switch (channels) {
+		case 1:
+			regval |= LPAIF_RDMACTL_WPSCNT_ONE;
+			break;
+		case 2:
+			regval |= LPAIF_RDMACTL_WPSCNT_TWO;
+			break;
+		case 4:
+			regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
+			break;
+		case 6:
+			regval |= LPAIF_RDMACTL_WPSCNT_SIX;
+			break;
+		case 8:
+			regval |= LPAIF_RDMACTL_WPSCNT_EIGHT;
+			break;
+		default:
+			dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
+					__func__, bitwidth, channels);
+			return -EINVAL;
+		}
+		break;
+	default:
+		dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
+				__func__, bitwidth, channels);
+		return -EINVAL;
+	}
+
+	ret = regmap_write(drvdata->lpaif_map,
+			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval);
+	if (ret) {
+		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_data *drvdata =
+		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	int ret;
+
+	ret = regmap_write(drvdata->lpaif_map,
+			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
+	if (ret)
+		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+				__func__, ret);
+
+	return ret;
+}
+
+static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_data *drvdata =
+		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	int ret;
+
+	ret = regmap_write(drvdata->lpaif_map,
+			LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S),
+			runtime->dma_addr);
+	if (ret) {
+		dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = regmap_write(drvdata->lpaif_map,
+			LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S),
+			(snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
+	if (ret) {
+		dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = regmap_write(drvdata->lpaif_map,
+			LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S),
+			(snd_pcm_lib_period_bytes(substream) >> 2) - 1);
+	if (ret) {
+		dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = regmap_update_bits(drvdata->lpaif_map,
+			LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+			LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
+	if (ret) {
+		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
+		int cmd)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_data *drvdata =
+		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	int ret;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		/* clear status before enabling interrupts */
+		ret = regmap_write(drvdata->lpaif_map,
+				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
+		if (ret) {
+			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
+					__func__, ret);
+			return ret;
+		}
+
+		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));
+		if (ret) {
+			dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
+					__func__, ret);
+			return ret;
+		}
+
+		ret = regmap_update_bits(drvdata->lpaif_map,
+				LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+				LPAIF_RDMACTL_ENABLE_MASK,
+				LPAIF_RDMACTL_ENABLE_ON);
+		if (ret) {
+			dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+					__func__, ret);
+			return ret;
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ret = regmap_update_bits(drvdata->lpaif_map,
+				LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+				LPAIF_RDMACTL_ENABLE_MASK,
+				LPAIF_RDMACTL_ENABLE_OFF);
+		if (ret) {
+			dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+					__func__, ret);
+			return ret;
+		}
+
+		ret = regmap_update_bits(drvdata->lpaif_map,
+				LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0);
+		if (ret) {
+			dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
+					__func__, ret);
+			return ret;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
+		struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct lpass_data *drvdata =
+			snd_soc_platform_get_drvdata(soc_runtime->platform);
+	unsigned int base_addr, curr_addr;
+	int ret;
+
+	ret = regmap_read(drvdata->lpaif_map,
+			LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr);
+	if (ret) {
+		dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = regmap_read(drvdata->lpaif_map,
+			LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr);
+	if (ret) {
+		dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	return bytes_to_frames(substream->runtime, curr_addr - base_addr);
+}
+
+static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
+		struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	return dma_mmap_coherent(substream->pcm->card->dev, vma,
+			runtime->dma_area, runtime->dma_addr,
+			runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops lpass_platform_pcm_ops = {
+	.open		= lpass_platform_pcmops_open,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= lpass_platform_pcmops_hw_params,
+	.hw_free	= lpass_platform_pcmops_hw_free,
+	.prepare	= lpass_platform_pcmops_prepare,
+	.trigger	= lpass_platform_pcmops_trigger,
+	.pointer	= lpass_platform_pcmops_pointer,
+	.mmap		= lpass_platform_pcmops_mmap,
+};
+
+static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
+{
+	struct snd_pcm_substream *substream = 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;
+	irqreturn_t ret = IRQ_NONE;
+	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)) {
+		rv = regmap_write(drvdata->lpaif_map,
+				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S));
+		if (rv) {
+			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
+					__func__, rv);
+			return IRQ_NONE;
+		}
+		snd_pcm_period_elapsed(substream);
+		ret = IRQ_HANDLED;
+	}
+
+	if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) {
+		rv = regmap_write(drvdata->lpaif_map,
+				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S));
+		if (rv) {
+			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
+					__func__, rv);
+			return IRQ_NONE;
+		}
+		dev_warn(soc_runtime->dev, "%s() xrun warning\n", __func__);
+		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+		ret = IRQ_HANDLED;
+	}
+
+	if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) {
+		rv = regmap_write(drvdata->lpaif_map,
+				LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
+				LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S));
+		if (rv) {
+			dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
+					__func__, rv);
+			return IRQ_NONE;
+		}
+		dev_err(soc_runtime->dev, "%s() bus access error\n", __func__);
+		snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
+		struct snd_soc_pcm_runtime *soc_runtime)
+{
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = soc_runtime->dev;
+	buf->private_data = NULL;
+	buf->area = dma_alloc_coherent(soc_runtime->dev, size, &buf->addr,
+			GFP_KERNEL);
+	if (!buf->area) {
+		dev_err(soc_runtime->dev, "%s: Could not allocate DMA buffer\n",
+				__func__);
+		return -ENOMEM;
+	}
+	buf->bytes = size;
+
+	return 0;
+}
+
+static void lpass_platform_free_buffer(struct snd_pcm_substream *substream,
+		struct snd_soc_pcm_runtime *soc_runtime)
+{
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+
+	if (buf->area) {
+		dma_free_coherent(soc_runtime->dev, buf->bytes, buf->area,
+				buf->addr);
+	}
+	buf->area = NULL;
+}
+
+static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
+{
+	struct snd_pcm *pcm = soc_runtime->pcm;
+	struct snd_pcm_substream *substream =
+		pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	struct lpass_data *drvdata =
+		snd_soc_platform_get_drvdata(soc_runtime->platform);
+	int ret;
+
+	soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+	soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask;
+
+	ret = lpass_platform_alloc_buffer(substream, soc_runtime);
+	if (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,
+			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);
+	if (ret) {
+		dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	return 0;
+
+err_buf:
+	lpass_platform_free_buffer(substream, soc_runtime);
+	return ret;
+}
+
+static void lpass_platform_pcm_free(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream =
+		pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+
+	lpass_platform_free_buffer(substream, soc_runtime);
+}
+
+static struct snd_soc_platform_driver lpass_platform_driver = {
+	.pcm_new	= lpass_platform_pcm_new,
+	.pcm_free	= lpass_platform_pcm_free,
+	.ops		= &lpass_platform_pcm_ops,
+};
+
+int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
+{
+	struct lpass_data *drvdata = platform_get_drvdata(pdev);
+
+	drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
+	if (drvdata->lpaif_irq < 0) {
+		dev_err(&pdev->dev, "%s() error getting irq handle: %d\n",
+				__func__, drvdata->lpaif_irq);
+		return -ENODEV;
+	}
+
+	return devm_snd_soc_register_platform(&pdev->dev,
+			&lpass_platform_driver);
+}
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
+
+MODULE_DESCRIPTION("QTi LPASS Platform Driver");
+MODULE_LICENSE("GPL v2");

+ 51 - 0
sound/soc/qcom/lpass.h

@@ -0,0 +1,51 @@
+/*
+ * 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.h - Definitions for the QTi LPASS
+ */
+
+#ifndef __LPASS_H__
+#define __LPASS_H__
+
+#include <linux/clk.h>
+#include <linux/compiler.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define LPASS_AHBIX_CLOCK_FREQUENCY		131072000
+
+/* Both the CPU DAI and platform drivers will access this data */
+struct lpass_data {
+
+	/* AHB-I/X bus clocks inside the low-power audio subsystem (LPASS) */
+	struct clk *ahbix_clk;
+
+	/* MI2S system clock */
+	struct clk *mi2s_osr_clk;
+
+	/* MI2S bit clock (derived from system clock by a divider */
+	struct clk *mi2s_bit_clk;
+
+	/* low-power audio interface (LPAIF) registers */
+	void __iomem *lpaif;
+
+	/* regmap backed by the low-power audio interface (LPAIF) registers */
+	struct regmap *lpaif_map;
+
+	/* interrupts from the low-power audio interface (LPAIF) */
+	int lpaif_irq;
+};
+
+/* register the platform driver from the CPU DAI driver */
+int asoc_qcom_lpass_platform_register(struct platform_device *);
+
+#endif /* __LPASS_H__ */

+ 162 - 0
sound/soc/qcom/storm.c

@@ -0,0 +1,162 @@
+/*
+ * 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.
+ *
+ * storm.c -- ALSA SoC machine driver for QTi ipq806x-based Storm board
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define STORM_SYSCLK_MULT			4
+
+static int storm_ops_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct snd_soc_card *card = soc_runtime->card;
+	snd_pcm_format_t format = params_format(params);
+	unsigned int rate = params_rate(params);
+	unsigned int sysclk_freq;
+	int bitwidth, ret;
+
+	bitwidth = snd_pcm_format_width(format);
+	if (bitwidth < 0) {
+		dev_err(card->dev, "%s() invalid bit width given: %d\n",
+				__func__, bitwidth);
+		return bitwidth;
+	}
+
+	/*
+	 * as the CPU DAI is the I2S bus master and no system clock is needed by
+	 * the MAX98357a DAC, simply set the system clock to be a constant
+	 * multiple of the bit clock for the clock divider
+	 */
+	sysclk_freq = rate * bitwidth * 2 * STORM_SYSCLK_MULT;
+
+	ret = snd_soc_dai_set_sysclk(soc_runtime->cpu_dai, 0, sysclk_freq, 0);
+	if (ret) {
+		dev_err(card->dev, "%s() error setting sysclk to %u: %d\n",
+				__func__, sysclk_freq, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops storm_soc_ops = {
+	.hw_params	= storm_ops_hw_params,
+};
+
+static struct snd_soc_dai_link storm_dai_link = {
+	.name		= "Primary",
+	.stream_name	= "Primary",
+	.codec_dai_name	= "HiFi",
+	.ops		= &storm_soc_ops,
+};
+
+static struct snd_soc_card storm_soc_card = {
+	.name	= "ipq806x-storm",
+	.dev	= NULL,
+};
+
+static int storm_parse_of(struct snd_soc_card *card)
+{
+	struct snd_soc_dai_link *dai_link = card->dai_link;
+	struct device_node *np = card->dev->of_node;
+
+	dai_link->cpu_of_node = of_parse_phandle(np, "cpu", 0);
+	if (!dai_link->cpu_of_node) {
+		dev_err(card->dev, "%s() error getting cpu phandle\n",
+				__func__);
+		return -EINVAL;
+	}
+	dai_link->platform_of_node = dai_link->cpu_of_node;
+
+	dai_link->codec_of_node = of_parse_phandle(np, "codec", 0);
+	if (!dai_link->codec_of_node) {
+		dev_err(card->dev, "%s() error getting codec phandle\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int storm_platform_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &storm_soc_card;
+	int ret;
+
+	if (card->dev) {
+		dev_err(&pdev->dev, "%s() error, existing soundcard\n",
+				__func__);
+		return -ENODEV;
+	}
+	card->dev = &pdev->dev;
+	platform_set_drvdata(pdev, card);
+
+	ret = snd_soc_of_parse_card_name(card, "qcom,model");
+	if (ret) {
+		dev_err(&pdev->dev, "%s() error parsing card name: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	card->dai_link	= &storm_dai_link;
+	card->num_links	= 1;
+
+	ret = storm_parse_of(card);
+	if (ret) {
+		dev_err(&pdev->dev, "%s() error resolving dai links: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+	if (ret == -EPROBE_DEFER) {
+		card->dev = NULL;
+		return ret;
+	} else if (ret) {
+		dev_err(&pdev->dev, "%s() error registering soundcard: %d\n",
+				__func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id storm_device_id[]  = {
+	{ .compatible = "google,storm-audio" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, storm_device_id);
+#endif
+
+static struct platform_driver storm_platform_driver = {
+	.driver = {
+		.name = "storm-audio",
+		.of_match_table =
+			of_match_ptr(storm_device_id),
+	},
+	.probe = storm_platform_probe,
+};
+module_platform_driver(storm_platform_driver);
+
+MODULE_DESCRIPTION("QTi IPQ806x-based Storm Machine Driver");
+MODULE_LICENSE("GPL v2");

+ 6 - 0
sound/soc/sh/Kconfig

@@ -36,11 +36,17 @@ config SND_SOC_SH4_SIU
 
 
 config SND_SOC_RCAR
 config SND_SOC_RCAR
 	tristate "R-Car series SRU/SCU/SSIU/SSI support"
 	tristate "R-Car series SRU/SCU/SSIU/SSI support"
+	depends on DMA_OF
 	select SND_SIMPLE_CARD
 	select SND_SIMPLE_CARD
 	select REGMAP_MMIO
 	select REGMAP_MMIO
 	help
 	help
 	  This option enables R-Car SUR/SCU/SSIU/SSI sound support
 	  This option enables R-Car SUR/SCU/SSIU/SSI sound support
 
 
+config SND_SOC_RSRC_CARD
+	tristate "Renesas Sampling Rate Convert Sound Card"
+	help
+	  This option enables simple sound if you need sampling rate convert
+
 ##
 ##
 ## Boards
 ## Boards
 ##
 ##

+ 5 - 2
sound/soc/sh/rcar/Makefile

@@ -1,2 +1,5 @@
-snd-soc-rcar-objs	:= core.o gen.o src.o adg.o ssi.o dvc.o
-obj-$(CONFIG_SND_SOC_RCAR)	+= snd-soc-rcar.o
+snd-soc-rcar-objs	:= core.o gen.o dma.o src.o adg.o ssi.o dvc.o
+obj-$(CONFIG_SND_SOC_RCAR)	+= snd-soc-rcar.o
+
+snd-soc-rsrc-card-objs	:= rsrc-card.o
+obj-$(CONFIG_SND_SOC_RSRC_CARD)	+= snd-soc-rsrc-card.o

+ 2 - 2
sound/soc/sh/rcar/adg.c

@@ -183,6 +183,8 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
 
 
 	rsnd_mod_bset(mod, DIV_EN, en, en);
 	rsnd_mod_bset(mod, DIV_EN, en, en);
 
 
+	dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate);
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -432,7 +434,5 @@ int rsnd_adg_probe(struct platform_device *pdev,
 
 
 	priv->adg = adg;
 	priv->adg = adg;
 
 
-	dev_dbg(dev, "adg probed\n");
-
 	return 0;
 	return 0;
 }
 }

+ 27 - 235
sound/soc/sh/rcar/core.c

@@ -94,21 +94,20 @@
  *
  *
  */
  */
 #include <linux/pm_runtime.h>
 #include <linux/pm_runtime.h>
-#include <linux/shdma-base.h>
 #include "rsnd.h"
 #include "rsnd.h"
 
 
 #define RSND_RATES SNDRV_PCM_RATE_8000_96000
 #define RSND_RATES SNDRV_PCM_RATE_8000_96000
 #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
 #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
 
 
-static struct rsnd_of_data rsnd_of_data_gen1 = {
+static const struct rsnd_of_data rsnd_of_data_gen1 = {
 	.flags = RSND_GEN1,
 	.flags = RSND_GEN1,
 };
 };
 
 
-static struct rsnd_of_data rsnd_of_data_gen2 = {
+static const struct rsnd_of_data rsnd_of_data_gen2 = {
 	.flags = RSND_GEN2,
 	.flags = RSND_GEN2,
 };
 };
 
 
-static struct of_device_id rsnd_of_match[] = {
+static const struct of_device_id rsnd_of_match[] = {
 	{ .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 },
 	{ .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 },
 	{ .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 },
 	{ .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 },
 	{},
 	{},
@@ -138,15 +137,12 @@ char *rsnd_mod_name(struct rsnd_mod *mod)
 	return mod->ops->name;
 	return mod->ops->name;
 }
 }
 
 
-char *rsnd_mod_dma_name(struct rsnd_mod *mod)
+struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod)
 {
 {
-	if (!mod || !mod->ops)
-		return "unknown";
-
-	if (!mod->ops->dma_name)
-		return mod->ops->name;
+	if (!mod || !mod->ops || !mod->ops->dma_req)
+		return NULL;
 
 
-	return mod->ops->dma_name(mod);
+	return mod->ops->dma_req(mod);
 }
 }
 
 
 int rsnd_mod_init(struct rsnd_mod *mod,
 int rsnd_mod_init(struct rsnd_mod *mod,
@@ -174,228 +170,6 @@ void rsnd_mod_quit(struct rsnd_mod *mod)
 		clk_unprepare(mod->clk);
 		clk_unprepare(mod->clk);
 }
 }
 
 
-/*
- *	rsnd_dma functions
- */
-void rsnd_dma_stop(struct rsnd_dma *dma)
-{
-	dmaengine_terminate_all(dma->chan);
-}
-
-static void rsnd_dma_complete(void *data)
-{
-	struct rsnd_dma *dma = (struct rsnd_dma *)data;
-	struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
-	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
-
-	/*
-	 * Renesas sound Gen1 needs 1 DMAC,
-	 * Gen2 needs 2 DMAC.
-	 * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri.
-	 * But, Audio-DMAC-peri-peri doesn't have interrupt,
-	 * and this driver is assuming that here.
-	 *
-	 * If Audio-DMAC-peri-peri has interrpt,
-	 * rsnd_dai_pointer_update() will be called twice,
-	 * ant it will breaks io->byte_pos
-	 */
-
-	rsnd_dai_pointer_update(io, io->byte_per_period);
-}
-
-void rsnd_dma_start(struct rsnd_dma *dma)
-{
-	struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
-	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
-	struct snd_pcm_substream *substream = io->substream;
-	struct device *dev = rsnd_priv_to_dev(priv);
-	struct dma_async_tx_descriptor *desc;
-
-	desc = dmaengine_prep_dma_cyclic(dma->chan,
-					 (dma->addr) ? dma->addr :
-					 substream->runtime->dma_addr,
-					 snd_pcm_lib_buffer_bytes(substream),
-					 snd_pcm_lib_period_bytes(substream),
-					 dma->dir,
-					 DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
-
-	if (!desc) {
-		dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
-		return;
-	}
-
-	desc->callback		= rsnd_dma_complete;
-	desc->callback_param	= dma;
-
-	if (dmaengine_submit(desc) < 0) {
-		dev_err(dev, "dmaengine_submit() fail\n");
-		return;
-	}
-
-	dma_async_issue_pending(dma->chan);
-}
-
-int rsnd_dma_available(struct rsnd_dma *dma)
-{
-	return !!dma->chan;
-}
-
-#define DMA_NAME_SIZE 16
-#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */
-static int _rsnd_dma_of_name(char *dma_name, struct rsnd_mod *mod)
-{
-	if (mod)
-		return snprintf(dma_name, DMA_NAME_SIZE / 2, "%s%d",
-			 rsnd_mod_dma_name(mod), rsnd_mod_id(mod));
-	else
-		return snprintf(dma_name, DMA_NAME_SIZE / 2, "mem");
-
-}
-
-static void rsnd_dma_of_name(struct rsnd_mod *mod_from,
-			     struct rsnd_mod *mod_to,
-			     char *dma_name)
-{
-	int index = 0;
-
-	index = _rsnd_dma_of_name(dma_name + index, mod_from);
-	*(dma_name + index++) = '_';
-	index = _rsnd_dma_of_name(dma_name + index, mod_to);
-}
-
-static void rsnd_dma_of_path(struct rsnd_dma *dma,
-			     int is_play,
-			     struct rsnd_mod **mod_from,
-			     struct rsnd_mod **mod_to)
-{
-	struct rsnd_mod *this = rsnd_dma_to_mod(dma);
-	struct rsnd_dai_stream *io = rsnd_mod_to_io(this);
-	struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
-	struct rsnd_mod *src = rsnd_io_to_mod_src(io);
-	struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
-	struct rsnd_mod *mod[MOD_MAX];
-	int i, index;
-
-
-	for (i = 0; i < MOD_MAX; i++)
-		mod[i] = NULL;
-
-	/*
-	 * in play case...
-	 *
-	 * src -> dst
-	 *
-	 * mem -> SSI
-	 * mem -> SRC -> SSI
-	 * mem -> SRC -> DVC -> SSI
-	 */
-	mod[0] = NULL; /* for "mem" */
-	index = 1;
-	for (i = 1; i < MOD_MAX; i++) {
-		if (!src) {
-			mod[i] = ssi;
-		} else if (!dvc) {
-			mod[i] = src;
-			src = NULL;
-		} else {
-			if ((!is_play) && (this == src))
-				this = dvc;
-
-			mod[i] = (is_play) ? src : dvc;
-			i++;
-			mod[i] = (is_play) ? dvc : src;
-			src = NULL;
-			dvc = NULL;
-		}
-
-		if (mod[i] == this)
-			index = i;
-
-		if (mod[i] == ssi)
-			break;
-	}
-
-	if (is_play) {
-		*mod_from = mod[index - 1];
-		*mod_to   = mod[index];
-	} else {
-		*mod_from = mod[index];
-		*mod_to   = mod[index - 1];
-	}
-}
-
-int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
-		  int is_play, int id)
-{
-	struct device *dev = rsnd_priv_to_dev(priv);
-	struct dma_slave_config cfg;
-	struct rsnd_mod *mod_from;
-	struct rsnd_mod *mod_to;
-	char dma_name[DMA_NAME_SIZE];
-	dma_cap_mask_t mask;
-	int ret;
-
-	if (dma->chan) {
-		dev_err(dev, "it already has dma channel\n");
-		return -EIO;
-	}
-
-	dma_cap_zero(mask);
-	dma_cap_set(DMA_SLAVE, mask);
-
-	rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to);
-	rsnd_dma_of_name(mod_from, mod_to, dma_name);
-
-	cfg.slave_id	= id;
-	cfg.direction	= is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
-	cfg.src_addr	= rsnd_gen_dma_addr(priv, mod_from, is_play, 1);
-	cfg.dst_addr	= rsnd_gen_dma_addr(priv, mod_to,   is_play, 0);
-	cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-	cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-
-	dev_dbg(dev, "dma : %s %pad -> %pad\n",
-		dma_name, &cfg.src_addr, &cfg.dst_addr);
-
-	dma->chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
-						     (void *)id, dev,
-						     dma_name);
-	if (!dma->chan) {
-		dev_err(dev, "can't get dma channel\n");
-		goto rsnd_dma_channel_err;
-	}
-
-	ret = dmaengine_slave_config(dma->chan, &cfg);
-	if (ret < 0)
-		goto rsnd_dma_init_err;
-
-	dma->addr = is_play ? cfg.src_addr : cfg.dst_addr;
-	dma->dir = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
-
-	return 0;
-
-rsnd_dma_init_err:
-	rsnd_dma_quit(priv, dma);
-rsnd_dma_channel_err:
-
-	/*
-	 * DMA failed. try to PIO mode
-	 * see
-	 *	rsnd_ssi_fallback()
-	 *	rsnd_rdai_continuance_probe()
-	 */
-	return -EAGAIN;
-}
-
-void  rsnd_dma_quit(struct rsnd_priv *priv,
-		    struct rsnd_dma *dma)
-{
-	if (dma->chan)
-		dma_release_channel(dma->chan);
-
-	dma->chan = NULL;
-}
-
 /*
 /*
  *	settting function
  *	settting function
  */
  */
@@ -429,7 +203,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
 ({								\
 ({								\
 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);		\
 	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);		\
 	struct device *dev = rsnd_priv_to_dev(priv);		\
 	struct device *dev = rsnd_priv_to_dev(priv);		\
-	u32 mask = 1 << __rsnd_mod_shift_##func;			\
+	u32 mask = (1 << __rsnd_mod_shift_##func) & ~(1 << 31);	\
 	u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func;	\
 	u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func;	\
 	int ret = 0;							\
 	int ret = 0;							\
 	if ((mod->status & mask) == call) {				\
 	if ((mod->status & mask) == call) {				\
@@ -471,7 +245,7 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
 		struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 		struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
 		struct device *dev = rsnd_priv_to_dev(priv);
 		struct device *dev = rsnd_priv_to_dev(priv);
 
 
-		dev_err(dev, "%s%d is not empty\n",
+		dev_err(dev, "%s[%d] is not empty\n",
 			rsnd_mod_name(mod),
 			rsnd_mod_name(mod),
 			rsnd_mod_id(mod));
 			rsnd_mod_id(mod));
 		return -EIO;
 		return -EIO;
@@ -887,20 +661,28 @@ static int rsnd_dai_probe(struct platform_device *pdev,
 		drv[i].name	= rdai[i].name;
 		drv[i].name	= rdai[i].name;
 		drv[i].ops	= &rsnd_soc_dai_ops;
 		drv[i].ops	= &rsnd_soc_dai_ops;
 		if (pmod) {
 		if (pmod) {
+			snprintf(rdai[i].playback.name, RSND_DAI_NAME_SIZE,
+				 "DAI%d Playback", i);
+
 			drv[i].playback.rates		= RSND_RATES;
 			drv[i].playback.rates		= RSND_RATES;
 			drv[i].playback.formats		= RSND_FMTS;
 			drv[i].playback.formats		= RSND_FMTS;
 			drv[i].playback.channels_min	= 2;
 			drv[i].playback.channels_min	= 2;
 			drv[i].playback.channels_max	= 2;
 			drv[i].playback.channels_max	= 2;
+			drv[i].playback.stream_name	= rdai[i].playback.name;
 
 
 			rdai[i].playback.info = &info->dai_info[i].playback;
 			rdai[i].playback.info = &info->dai_info[i].playback;
 			rdai[i].playback.rdai = rdai + i;
 			rdai[i].playback.rdai = rdai + i;
 			rsnd_path_init(priv, &rdai[i], &rdai[i].playback);
 			rsnd_path_init(priv, &rdai[i], &rdai[i].playback);
 		}
 		}
 		if (cmod) {
 		if (cmod) {
+			snprintf(rdai[i].capture.name, RSND_DAI_NAME_SIZE,
+				 "DAI%d Capture", i);
+
 			drv[i].capture.rates		= RSND_RATES;
 			drv[i].capture.rates		= RSND_RATES;
 			drv[i].capture.formats		= RSND_FMTS;
 			drv[i].capture.formats		= RSND_FMTS;
 			drv[i].capture.channels_min	= 2;
 			drv[i].capture.channels_min	= 2;
 			drv[i].capture.channels_max	= 2;
 			drv[i].capture.channels_max	= 2;
+			drv[i].capture.stream_name	= rdai[i].capture.name;
 
 
 			rdai[i].capture.info = &info->dai_info[i].capture;
 			rdai[i].capture.info = &info->dai_info[i].capture;
 			rdai[i].capture.rdai = rdai + i;
 			rdai[i].capture.rdai = rdai + i;
@@ -946,6 +728,15 @@ static int rsnd_pcm_open(struct snd_pcm_substream *substream)
 static int rsnd_hw_params(struct snd_pcm_substream *substream,
 static int rsnd_hw_params(struct snd_pcm_substream *substream,
 			 struct snd_pcm_hw_params *hw_params)
 			 struct snd_pcm_hw_params *hw_params)
 {
 {
+	struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
+	struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+	struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+	int ret;
+
+	ret = rsnd_dai_call(hw_params, io, substream, hw_params);
+	if (ret)
+		return ret;
+
 	return snd_pcm_lib_malloc_pages(substream,
 	return snd_pcm_lib_malloc_pages(substream,
 					params_buffer_bytes(hw_params));
 					params_buffer_bytes(hw_params));
 }
 }
@@ -1210,6 +1001,7 @@ static int rsnd_probe(struct platform_device *pdev)
 			    const struct rsnd_of_data *of_data,
 			    const struct rsnd_of_data *of_data,
 			    struct rsnd_priv *priv) = {
 			    struct rsnd_priv *priv) = {
 		rsnd_gen_probe,
 		rsnd_gen_probe,
+		rsnd_dma_probe,
 		rsnd_ssi_probe,
 		rsnd_ssi_probe,
 		rsnd_src_probe,
 		rsnd_src_probe,
 		rsnd_dvc_probe,
 		rsnd_dvc_probe,

+ 616 - 0
sound/soc/sh/rcar/dma.c

@@ -0,0 +1,616 @@
+/*
+ * Renesas R-Car Audio DMAC support
+ *
+ * Copyright (C) 2015 Renesas Electronics Corp.
+ * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/delay.h>
+#include <linux/of_dma.h>
+#include "rsnd.h"
+
+/*
+ * Audio DMAC peri peri register
+ */
+#define PDMASAR		0x00
+#define PDMADAR		0x04
+#define PDMACHCR	0x0c
+
+/* PDMACHCR */
+#define PDMACHCR_DE		(1 << 0)
+
+struct rsnd_dma_ctrl {
+	void __iomem *base;
+	int dmapp_num;
+};
+
+#define rsnd_priv_to_dmac(p)	((struct rsnd_dma_ctrl *)(p)->dma)
+
+/*
+ *		Audio DMAC
+ */
+static void rsnd_dmaen_complete(void *data)
+{
+	struct rsnd_dma *dma = (struct rsnd_dma *)data;
+	struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+
+	/*
+	 * Renesas sound Gen1 needs 1 DMAC,
+	 * Gen2 needs 2 DMAC.
+	 * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri.
+	 * But, Audio-DMAC-peri-peri doesn't have interrupt,
+	 * and this driver is assuming that here.
+	 *
+	 * If Audio-DMAC-peri-peri has interrpt,
+	 * rsnd_dai_pointer_update() will be called twice,
+	 * ant it will breaks io->byte_pos
+	 */
+
+	rsnd_dai_pointer_update(io, io->byte_per_period);
+}
+
+static void rsnd_dmaen_stop(struct rsnd_dma *dma)
+{
+	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+
+	dmaengine_terminate_all(dmaen->chan);
+}
+
+static void rsnd_dmaen_start(struct rsnd_dma *dma)
+{
+	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+	struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	struct snd_pcm_substream *substream = io->substream;
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct dma_async_tx_descriptor *desc;
+	int is_play = rsnd_io_is_play(io);
+
+	desc = dmaengine_prep_dma_cyclic(dmaen->chan,
+					 substream->runtime->dma_addr,
+					 snd_pcm_lib_buffer_bytes(substream),
+					 snd_pcm_lib_period_bytes(substream),
+					 is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+					 DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
+	if (!desc) {
+		dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
+		return;
+	}
+
+	desc->callback		= rsnd_dmaen_complete;
+	desc->callback_param	= dma;
+
+	if (dmaengine_submit(desc) < 0) {
+		dev_err(dev, "dmaengine_submit() fail\n");
+		return;
+	}
+
+	dma_async_issue_pending(dmaen->chan);
+}
+
+struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
+					  struct rsnd_mod *mod, char *name)
+{
+	struct dma_chan *chan;
+	struct device_node *np;
+	int i = 0;
+
+	for_each_child_of_node(of_node, np) {
+		if (i == rsnd_mod_id(mod))
+			break;
+		i++;
+	}
+
+	chan = of_dma_request_slave_channel(np, name);
+
+	of_node_put(np);
+	of_node_put(of_node);
+
+	return chan;
+}
+
+static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from,
+						   struct rsnd_mod *mod_to)
+{
+	if ((!mod_from && !mod_to) ||
+	    (mod_from && mod_to))
+		return NULL;
+
+	if (mod_from)
+		return rsnd_mod_dma_req(mod_from);
+	else
+		return rsnd_mod_dma_req(mod_to);
+}
+
+static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
+			   struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
+{
+	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct dma_slave_config cfg = {};
+	struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	int is_play = rsnd_io_is_play(io);
+	int ret;
+
+	if (dmaen->chan) {
+		dev_err(dev, "it already has dma channel\n");
+		return -EIO;
+	}
+
+	if (dev->of_node) {
+		dmaen->chan = rsnd_dmaen_request_channel(mod_from, mod_to);
+	} else {
+		dma_cap_mask_t mask;
+
+		dma_cap_zero(mask);
+		dma_cap_set(DMA_SLAVE, mask);
+
+		dmaen->chan = dma_request_channel(mask, shdma_chan_filter,
+						  (void *)id);
+	}
+	if (IS_ERR_OR_NULL(dmaen->chan)) {
+		dev_err(dev, "can't get dma channel\n");
+		goto rsnd_dma_channel_err;
+	}
+
+	cfg.direction	= is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+	cfg.src_addr	= dma->src_addr;
+	cfg.dst_addr	= dma->dst_addr;
+	cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+	dev_dbg(dev, "dma : %pad -> %pad\n",
+		&cfg.src_addr, &cfg.dst_addr);
+
+	ret = dmaengine_slave_config(dmaen->chan, &cfg);
+	if (ret < 0)
+		goto rsnd_dma_init_err;
+
+	return 0;
+
+rsnd_dma_init_err:
+	rsnd_dma_quit(dma);
+rsnd_dma_channel_err:
+
+	/*
+	 * DMA failed. try to PIO mode
+	 * see
+	 *	rsnd_ssi_fallback()
+	 *	rsnd_rdai_continuance_probe()
+	 */
+	return -EAGAIN;
+}
+
+static void rsnd_dmaen_quit(struct rsnd_dma *dma)
+{
+	struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+
+	if (dmaen->chan)
+		dma_release_channel(dmaen->chan);
+
+	dmaen->chan = NULL;
+}
+
+static struct rsnd_dma_ops rsnd_dmaen_ops = {
+	.start	= rsnd_dmaen_start,
+	.stop	= rsnd_dmaen_stop,
+	.init	= rsnd_dmaen_init,
+	.quit	= rsnd_dmaen_quit,
+};
+
+/*
+ *		Audio DMAC peri peri
+ */
+static const u8 gen2_id_table_ssiu[] = {
+	0x00, /* SSI00 */
+	0x04, /* SSI10 */
+	0x08, /* SSI20 */
+	0x0c, /* SSI3  */
+	0x0d, /* SSI4  */
+	0x0e, /* SSI5  */
+	0x0f, /* SSI6  */
+	0x10, /* SSI7  */
+	0x11, /* SSI8  */
+	0x12, /* SSI90 */
+};
+static const u8 gen2_id_table_scu[] = {
+	0x2d, /* SCU_SRCI0 */
+	0x2e, /* SCU_SRCI1 */
+	0x2f, /* SCU_SRCI2 */
+	0x30, /* SCU_SRCI3 */
+	0x31, /* SCU_SRCI4 */
+	0x32, /* SCU_SRCI5 */
+	0x33, /* SCU_SRCI6 */
+	0x34, /* SCU_SRCI7 */
+	0x35, /* SCU_SRCI8 */
+	0x36, /* SCU_SRCI9 */
+};
+static const u8 gen2_id_table_cmd[] = {
+	0x37, /* SCU_CMD0 */
+	0x38, /* SCU_CMD1 */
+};
+
+static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod)
+{
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+	struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+	struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
+	const u8 *entry = NULL;
+	int id = rsnd_mod_id(mod);
+	int size = 0;
+
+	if (mod == ssi) {
+		entry = gen2_id_table_ssiu;
+		size = ARRAY_SIZE(gen2_id_table_ssiu);
+	} else if (mod == src) {
+		entry = gen2_id_table_scu;
+		size = ARRAY_SIZE(gen2_id_table_scu);
+	} else if (mod == dvc) {
+		entry = gen2_id_table_cmd;
+		size = ARRAY_SIZE(gen2_id_table_cmd);
+	}
+
+	if (!entry)
+		return 0xFF;
+
+	if (size <= id)
+		return 0xFF;
+
+	return entry[id];
+}
+
+static u32 rsnd_dmapp_get_chcr(struct rsnd_mod *mod_from,
+			       struct rsnd_mod *mod_to)
+{
+	return	(rsnd_dmapp_get_id(mod_from) << 24) +
+		(rsnd_dmapp_get_id(mod_to) << 16);
+}
+
+#define rsnd_dmapp_addr(dmac, dma, reg) \
+	(dmac->base + 0x20 + reg + \
+	 (0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id))
+static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg)
+{
+	struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+
+	dev_dbg(dev, "w %p : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data);
+
+	iowrite32(data, rsnd_dmapp_addr(dmac, dma, reg));
+}
+
+static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg)
+{
+	struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+
+	return ioread32(rsnd_dmapp_addr(dmac, dma, reg));
+}
+
+static void rsnd_dmapp_stop(struct rsnd_dma *dma)
+{
+	int i;
+
+	rsnd_dmapp_write(dma, 0, PDMACHCR);
+
+	for (i = 0; i < 1024; i++) {
+		if (0 == rsnd_dmapp_read(dma, PDMACHCR))
+			return;
+		udelay(1);
+	}
+}
+
+static void rsnd_dmapp_start(struct rsnd_dma *dma)
+{
+	struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
+
+	rsnd_dmapp_write(dma, dma->src_addr,	PDMASAR);
+	rsnd_dmapp_write(dma, dma->dst_addr,	PDMADAR);
+	rsnd_dmapp_write(dma, dmapp->chcr,	PDMACHCR);
+}
+
+static int rsnd_dmapp_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
+			   struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
+{
+	struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
+	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+	struct device *dev = rsnd_priv_to_dev(priv);
+
+	dmapp->dmapp_id = dmac->dmapp_num;
+	dmapp->chcr = rsnd_dmapp_get_chcr(mod_from, mod_to) | PDMACHCR_DE;
+
+	dmac->dmapp_num++;
+
+	rsnd_dmapp_stop(dma);
+
+	dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n",
+		dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr);
+
+	return 0;
+}
+
+static struct rsnd_dma_ops rsnd_dmapp_ops = {
+	.start	= rsnd_dmapp_start,
+	.stop	= rsnd_dmapp_stop,
+	.init	= rsnd_dmapp_init,
+	.quit	= rsnd_dmapp_stop,
+};
+
+/*
+ *		Common DMAC Interface
+ */
+
+/*
+ *	DMA read/write register offset
+ *
+ *	RSND_xxx_I_N	for Audio DMAC input
+ *	RSND_xxx_O_N	for Audio DMAC output
+ *	RSND_xxx_I_P	for Audio DMAC peri peri input
+ *	RSND_xxx_O_P	for Audio DMAC peri peri output
+ *
+ *	ex) R-Car H2 case
+ *	      mod        / DMAC in    / DMAC out   / DMAC PP in / DMAC pp out
+ *	SSI : 0xec541000 / 0xec241008 / 0xec24100c
+ *	SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000
+ *	SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000
+ *	CMD : 0xec500000 /            / 0xec008000                0xec308000
+ */
+#define RDMA_SSI_I_N(addr, i)	(addr ##_reg - 0x00300000 + (0x40 * i) + 0x8)
+#define RDMA_SSI_O_N(addr, i)	(addr ##_reg - 0x00300000 + (0x40 * i) + 0xc)
+
+#define RDMA_SSIU_I_N(addr, i)	(addr ##_reg - 0x00441000 + (0x1000 * i))
+#define RDMA_SSIU_O_N(addr, i)	(addr ##_reg - 0x00441000 + (0x1000 * i))
+
+#define RDMA_SSIU_I_P(addr, i)	(addr ##_reg - 0x00141000 + (0x1000 * i))
+#define RDMA_SSIU_O_P(addr, i)	(addr ##_reg - 0x00141000 + (0x1000 * i))
+
+#define RDMA_SRC_I_N(addr, i)	(addr ##_reg - 0x00500000 + (0x400 * i))
+#define RDMA_SRC_O_N(addr, i)	(addr ##_reg - 0x004fc000 + (0x400 * i))
+
+#define RDMA_SRC_I_P(addr, i)	(addr ##_reg - 0x00200000 + (0x400 * i))
+#define RDMA_SRC_O_P(addr, i)	(addr ##_reg - 0x001fc000 + (0x400 * i))
+
+#define RDMA_CMD_O_N(addr, i)	(addr ##_reg - 0x004f8000 + (0x400 * i))
+#define RDMA_CMD_O_P(addr, i)	(addr ##_reg - 0x001f8000 + (0x400 * i))
+
+static dma_addr_t
+rsnd_gen2_dma_addr(struct rsnd_priv *priv,
+		   struct rsnd_mod *mod,
+		   int is_play, int is_from)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI);
+	phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU);
+	int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod);
+	int use_src = !!rsnd_io_to_mod_src(io);
+	int use_dvc = !!rsnd_io_to_mod_dvc(io);
+	int id = rsnd_mod_id(mod);
+	struct dma_addr {
+		dma_addr_t out_addr;
+		dma_addr_t in_addr;
+	} dma_addrs[3][2][3] = {
+		/* SRC */
+		{{{ 0,				0 },
+		  /* Capture */
+		  { RDMA_SRC_O_N(src, id),	RDMA_SRC_I_P(src, id) },
+		  { RDMA_CMD_O_N(src, id),	RDMA_SRC_I_P(src, id) } },
+		 /* Playback */
+		 {{ 0,				0, },
+		  { RDMA_SRC_O_P(src, id),	RDMA_SRC_I_N(src, id) },
+		  { RDMA_CMD_O_P(src, id),	RDMA_SRC_I_N(src, id) } }
+		},
+		/* SSI */
+		/* Capture */
+		{{{ RDMA_SSI_O_N(ssi, id),	0 },
+		  { RDMA_SSIU_O_P(ssi, id),	0 },
+		  { RDMA_SSIU_O_P(ssi, id),	0 } },
+		 /* Playback */
+		 {{ 0,				RDMA_SSI_I_N(ssi, id) },
+		  { 0,				RDMA_SSIU_I_P(ssi, id) },
+		  { 0,				RDMA_SSIU_I_P(ssi, id) } }
+		},
+		/* SSIU */
+		/* Capture */
+		{{{ RDMA_SSIU_O_N(ssi, id),	0 },
+		  { RDMA_SSIU_O_P(ssi, id),	0 },
+		  { RDMA_SSIU_O_P(ssi, id),	0 } },
+		 /* Playback */
+		 {{ 0,				RDMA_SSIU_I_N(ssi, id) },
+		  { 0,				RDMA_SSIU_I_P(ssi, id) },
+		  { 0,				RDMA_SSIU_I_P(ssi, id) } } },
+	};
+
+	/* it shouldn't happen */
+	if (use_dvc && !use_src)
+		dev_err(dev, "DVC is selected without SRC\n");
+
+	/* use SSIU or SSI ? */
+	if (is_ssi && rsnd_ssi_use_busif(mod))
+		is_ssi++;
+
+	return (is_from) ?
+		dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr :
+		dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr;
+}
+
+static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv,
+				struct rsnd_mod *mod,
+				int is_play, int is_from)
+{
+	/*
+	 * gen1 uses default DMA addr
+	 */
+	if (rsnd_is_gen1(priv))
+		return 0;
+
+	if (!mod)
+		return 0;
+
+	return rsnd_gen2_dma_addr(priv, mod, is_play, is_from);
+}
+
+#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */
+static void rsnd_dma_of_path(struct rsnd_dma *dma,
+			     int is_play,
+			     struct rsnd_mod **mod_from,
+			     struct rsnd_mod **mod_to)
+{
+	struct rsnd_mod *this = rsnd_dma_to_mod(dma);
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(this);
+	struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+	struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+	struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
+	struct rsnd_mod *mod[MOD_MAX];
+	int i, index;
+
+
+	for (i = 0; i < MOD_MAX; i++)
+		mod[i] = NULL;
+
+	/*
+	 * in play case...
+	 *
+	 * src -> dst
+	 *
+	 * mem -> SSI
+	 * mem -> SRC -> SSI
+	 * mem -> SRC -> DVC -> SSI
+	 */
+	mod[0] = NULL; /* for "mem" */
+	index = 1;
+	for (i = 1; i < MOD_MAX; i++) {
+		if (!src) {
+			mod[i] = ssi;
+		} else if (!dvc) {
+			mod[i] = src;
+			src = NULL;
+		} else {
+			if ((!is_play) && (this == src))
+				this = dvc;
+
+			mod[i] = (is_play) ? src : dvc;
+			i++;
+			mod[i] = (is_play) ? dvc : src;
+			src = NULL;
+			dvc = NULL;
+		}
+
+		if (mod[i] == this)
+			index = i;
+
+		if (mod[i] == ssi)
+			break;
+	}
+
+	if (is_play) {
+		*mod_from = mod[index - 1];
+		*mod_to   = mod[index];
+	} else {
+		*mod_from = mod[index];
+		*mod_to   = mod[index - 1];
+	}
+}
+
+void rsnd_dma_stop(struct rsnd_dma *dma)
+{
+	dma->ops->stop(dma);
+}
+
+void rsnd_dma_start(struct rsnd_dma *dma)
+{
+	dma->ops->start(dma);
+}
+
+void rsnd_dma_quit(struct rsnd_dma *dma)
+{
+	struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+
+	if (!dmac)
+		return;
+
+	dma->ops->quit(dma);
+}
+
+int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id)
+{
+	struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+	struct rsnd_mod *mod_from;
+	struct rsnd_mod *mod_to;
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+	int is_play = rsnd_io_is_play(io);
+
+	/*
+	 * DMA failed. try to PIO mode
+	 * see
+	 *	rsnd_ssi_fallback()
+	 *	rsnd_rdai_continuance_probe()
+	 */
+	if (!dmac)
+		return -EAGAIN;
+
+	rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to);
+
+	dma->src_addr = rsnd_dma_addr(priv, mod_from, is_play, 1);
+	dma->dst_addr = rsnd_dma_addr(priv, mod_to,   is_play, 0);
+
+	/* for Gen2 */
+	if (mod_from && mod_to)
+		dma->ops = &rsnd_dmapp_ops;
+	else
+		dma->ops = &rsnd_dmaen_ops;
+
+	/* for Gen1, overwrite */
+	if (rsnd_is_gen1(priv))
+		dma->ops = &rsnd_dmaen_ops;
+
+	return dma->ops->init(priv, dma, id, mod_from, mod_to);
+}
+
+int rsnd_dma_probe(struct platform_device *pdev,
+		   const struct rsnd_of_data *of_data,
+		   struct rsnd_priv *priv)
+{
+	struct device *dev = rsnd_priv_to_dev(priv);
+	struct rsnd_dma_ctrl *dmac;
+	struct resource *res;
+
+	/*
+	 * for Gen1
+	 */
+	if (rsnd_is_gen1(priv))
+		return 0;
+
+	/*
+	 * for Gen2
+	 */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp");
+	dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL);
+	if (!dmac || !res) {
+		dev_err(dev, "dma allocate failed\n");
+		return 0; /* it will be PIO mode */
+	}
+
+	dmac->dmapp_num = 0;
+	dmac->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dmac->base))
+		return PTR_ERR(dmac->base);
+
+	priv->dma = dmac;
+
+	return 0;
+}

+ 13 - 15
sound/soc/sh/rcar/dvc.c

@@ -24,6 +24,9 @@ struct rsnd_dvc {
 	struct rsnd_kctrl_cfg_s rdown;	/* Ramp Rate Down */
 	struct rsnd_kctrl_cfg_s rdown;	/* Ramp Rate Down */
 };
 };
 
 
+#define rsnd_dvc_of_node(priv) \
+	of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
+
 #define rsnd_mod_to_dvc(_mod)	\
 #define rsnd_mod_to_dvc(_mod)	\
 	container_of((_mod), struct rsnd_dvc, mod)
 	container_of((_mod), struct rsnd_dvc, mod)
 
 
@@ -33,7 +36,7 @@ struct rsnd_dvc {
 	     ((pos) = (struct rsnd_dvc *)(priv)->dvc + i);	\
 	     ((pos) = (struct rsnd_dvc *)(priv)->dvc + i);	\
 	     i++)
 	     i++)
 
 
-static const char const *dvc_ramp_rate[] = {
+static const char * const dvc_ramp_rate[] = {
 	"128 dB/1 step",	 /* 00000 */
 	"128 dB/1 step",	 /* 00000 */
 	"64 dB/1 step",		 /* 00001 */
 	"64 dB/1 step",		 /* 00001 */
 	"32 dB/1 step",		 /* 00010 */
 	"32 dB/1 step",		 /* 00010 */
@@ -116,17 +119,6 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
 	rsnd_mod_write(mod, DVC_DVUER, 1);
 	rsnd_mod_write(mod, DVC_DVUER, 1);
 }
 }
 
 
-static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
-			       struct rsnd_priv *priv)
-{
-	struct device *dev = rsnd_priv_to_dev(priv);
-
-	dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
-		rsnd_mod_name(mod), rsnd_mod_id(mod));
-
-	return 0;
-}
-
 static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod,
 static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod,
 				struct rsnd_priv *priv)
 				struct rsnd_priv *priv)
 {
 {
@@ -269,9 +261,17 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
 	return 0;
 	return 0;
 }
 }
 
 
+static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_mod *mod)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+
+	return rsnd_dma_request_channel(rsnd_dvc_of_node(priv),
+					mod, "tx");
+}
+
 static struct rsnd_mod_ops rsnd_dvc_ops = {
 static struct rsnd_mod_ops rsnd_dvc_ops = {
 	.name		= DVC_NAME,
 	.name		= DVC_NAME,
-	.probe		= rsnd_dvc_probe_gen2,
+	.dma_req	= rsnd_dvc_dma_req,
 	.remove		= rsnd_dvc_remove_gen2,
 	.remove		= rsnd_dvc_remove_gen2,
 	.init		= rsnd_dvc_init,
 	.init		= rsnd_dvc_init,
 	.quit		= rsnd_dvc_quit,
 	.quit		= rsnd_dvc_quit,
@@ -370,8 +370,6 @@ int rsnd_dvc_probe(struct platform_device *pdev,
 			      clk, RSND_MOD_DVC, i);
 			      clk, RSND_MOD_DVC, i);
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
-
-		dev_dbg(dev, "CMD%d probed\n", i);
 	}
 	}
 
 
 	return 0;
 	return 0;

+ 23 - 129
sound/soc/sh/rcar/gen.c

@@ -28,6 +28,7 @@ struct rsnd_gen {
 
 
 	struct regmap *regmap[RSND_BASE_MAX];
 	struct regmap *regmap[RSND_BASE_MAX];
 	struct regmap_field *regs[RSND_REG_MAX];
 	struct regmap_field *regs[RSND_REG_MAX];
+	phys_addr_t res[RSND_REG_MAX];
 };
 };
 
 
 #define rsnd_priv_to_gen(p)	((struct rsnd_gen *)(p)->gen)
 #define rsnd_priv_to_gen(p)	((struct rsnd_gen *)(p)->gen)
@@ -118,11 +119,19 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
 				  mask, data);
 				  mask, data);
 }
 }
 
 
-#define rsnd_gen_regmap_init(priv, id_size, reg_id, conf)		\
-	_rsnd_gen_regmap_init(priv, id_size, reg_id, conf, ARRAY_SIZE(conf))
+phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id)
+{
+	struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+	return	gen->res[reg_id];
+}
+
+#define rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf)		\
+	_rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf, ARRAY_SIZE(conf))
 static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
 static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
 				 int id_size,
 				 int id_size,
 				 int reg_id,
 				 int reg_id,
+				 const char *name,
 				 struct rsnd_regmap_field_conf *conf,
 				 struct rsnd_regmap_field_conf *conf,
 				 int conf_size)
 				 int conf_size)
 {
 {
@@ -141,8 +150,11 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
 	regc.reg_bits = 32;
 	regc.reg_bits = 32;
 	regc.val_bits = 32;
 	regc.val_bits = 32;
 	regc.reg_stride = 4;
 	regc.reg_stride = 4;
+	regc.name = name;
 
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id);
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+	if (!res)
+		res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id);
 	if (!res)
 	if (!res)
 		return -ENODEV;
 		return -ENODEV;
 
 
@@ -156,6 +168,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
 
 
 	gen->base[reg_id] = base;
 	gen->base[reg_id] = base;
 	gen->regmap[reg_id] = regmap;
 	gen->regmap[reg_id] = regmap;
+	gen->res[reg_id] = res->start;
 
 
 	for (i = 0; i < conf_size; i++) {
 	for (i = 0; i < conf_size; i++) {
 
 
@@ -175,126 +188,12 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
 	return 0;
 	return 0;
 }
 }
 
 
-/*
- *	DMA read/write register offset
- *
- *	RSND_xxx_I_N	for Audio DMAC input
- *	RSND_xxx_O_N	for Audio DMAC output
- *	RSND_xxx_I_P	for Audio DMAC peri peri input
- *	RSND_xxx_O_P	for Audio DMAC peri peri output
- *
- *	ex) R-Car H2 case
- *	      mod        / DMAC in    / DMAC out   / DMAC PP in / DMAC pp out
- *	SSI : 0xec541000 / 0xec241008 / 0xec24100c
- *	SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000
- *	SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000
- *	CMD : 0xec500000 /            / 0xec008000                0xec308000
- */
-#define RDMA_SSI_I_N(addr, i)	(addr ##_reg - 0x00300000 + (0x40 * i) + 0x8)
-#define RDMA_SSI_O_N(addr, i)	(addr ##_reg - 0x00300000 + (0x40 * i) + 0xc)
-
-#define RDMA_SSIU_I_N(addr, i)	(addr ##_reg - 0x00441000 + (0x1000 * i))
-#define RDMA_SSIU_O_N(addr, i)	(addr ##_reg - 0x00441000 + (0x1000 * i))
-
-#define RDMA_SSIU_I_P(addr, i)	(addr ##_reg - 0x00141000 + (0x1000 * i))
-#define RDMA_SSIU_O_P(addr, i)	(addr ##_reg - 0x00141000 + (0x1000 * i))
-
-#define RDMA_SRC_I_N(addr, i)	(addr ##_reg - 0x00500000 + (0x400 * i))
-#define RDMA_SRC_O_N(addr, i)	(addr ##_reg - 0x004fc000 + (0x400 * i))
-
-#define RDMA_SRC_I_P(addr, i)	(addr ##_reg - 0x00200000 + (0x400 * i))
-#define RDMA_SRC_O_P(addr, i)	(addr ##_reg - 0x001fc000 + (0x400 * i))
-
-#define RDMA_CMD_O_N(addr, i)	(addr ##_reg - 0x004f8000 + (0x400 * i))
-#define RDMA_CMD_O_P(addr, i)	(addr ##_reg - 0x001f8000 + (0x400 * i))
-
-static dma_addr_t
-rsnd_gen2_dma_addr(struct rsnd_priv *priv,
-		   struct rsnd_mod *mod,
-		   int is_play, int is_from)
-{
-	struct platform_device *pdev = rsnd_priv_to_pdev(priv);
-	struct device *dev = rsnd_priv_to_dev(priv);
-	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
-	dma_addr_t ssi_reg = platform_get_resource(pdev,
-				IORESOURCE_MEM, RSND_GEN2_SSI)->start;
-	dma_addr_t src_reg = platform_get_resource(pdev,
-				IORESOURCE_MEM, RSND_GEN2_SCU)->start;
-	int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod);
-	int use_src = !!rsnd_io_to_mod_src(io);
-	int use_dvc = !!rsnd_io_to_mod_dvc(io);
-	int id = rsnd_mod_id(mod);
-	struct dma_addr {
-		dma_addr_t out_addr;
-		dma_addr_t in_addr;
-	} dma_addrs[3][2][3] = {
-		/* SRC */
-		{{{ 0,				0 },
-		/* Capture */
-		  { RDMA_SRC_O_N(src, id),	RDMA_SRC_I_P(src, id) },
-		  { RDMA_CMD_O_N(src, id),	RDMA_SRC_I_P(src, id) } },
-		 /* Playback */
-		 {{ 0,				0, },
-		  { RDMA_SRC_O_P(src, id),	RDMA_SRC_I_N(src, id) },
-		  { RDMA_CMD_O_P(src, id),	RDMA_SRC_I_N(src, id) } }
-		},
-		/* SSI */
-		/* Capture */
-		{{{ RDMA_SSI_O_N(ssi, id),	0 },
-		  { RDMA_SSIU_O_P(ssi, id),	0 },
-		  { RDMA_SSIU_O_P(ssi, id),	0 } },
-		 /* Playback */
-		 {{ 0,				RDMA_SSI_I_N(ssi, id) },
-		  { 0,				RDMA_SSIU_I_P(ssi, id) },
-		  { 0,				RDMA_SSIU_I_P(ssi, id) } }
-		},
-		/* SSIU */
-		/* Capture */
-		{{{ RDMA_SSIU_O_N(ssi, id),	0 },
-		  { RDMA_SSIU_O_P(ssi, id),	0 },
-		  { RDMA_SSIU_O_P(ssi, id),	0 } },
-		 /* Playback */
-		 {{ 0,				RDMA_SSIU_I_N(ssi, id) },
-		  { 0,				RDMA_SSIU_I_P(ssi, id) },
-		  { 0,				RDMA_SSIU_I_P(ssi, id) } } },
-	};
-
-	/* it shouldn't happen */
-	if (use_dvc && !use_src)
-		dev_err(dev, "DVC is selected without SRC\n");
-
-	/* use SSIU or SSI ? */
-	if (is_ssi && (0 == strcmp(rsnd_mod_dma_name(mod), "ssiu")))
-		is_ssi++;
-
-	return (is_from) ?
-		dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr :
-		dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr;
-}
-
-dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv,
-			     struct rsnd_mod *mod,
-			     int is_play, int is_from)
-{
-	/*
-	 * gen1 uses default DMA addr
-	 */
-	if (rsnd_is_gen1(priv))
-		return 0;
-
-	if (!mod)
-		return 0;
-
-	return rsnd_gen2_dma_addr(priv, mod, is_play, is_from);
-}
-
 /*
 /*
  *		Gen2
  *		Gen2
  */
  */
 static int rsnd_gen2_probe(struct platform_device *pdev,
 static int rsnd_gen2_probe(struct platform_device *pdev,
 			   struct rsnd_priv *priv)
 			   struct rsnd_priv *priv)
 {
 {
-	struct device *dev = rsnd_priv_to_dev(priv);
 	struct rsnd_regmap_field_conf conf_ssiu[] = {
 	struct rsnd_regmap_field_conf conf_ssiu[] = {
 		RSND_GEN_S_REG(SSI_MODE0,	0x800),
 		RSND_GEN_S_REG(SSI_MODE0,	0x800),
 		RSND_GEN_S_REG(SSI_MODE1,	0x804),
 		RSND_GEN_S_REG(SSI_MODE1,	0x804),
@@ -368,18 +267,16 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
 	int ret_adg;
 	int ret_adg;
 	int ret_ssi;
 	int ret_ssi;
 
 
-	ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, conf_ssiu);
-	ret_scu  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU,  conf_scu);
-	ret_adg  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG,  conf_adg);
-	ret_ssi  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI,  conf_ssi);
+	ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, "ssiu", conf_ssiu);
+	ret_scu  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU,  "scu",  conf_scu);
+	ret_adg  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG,  "adg",  conf_adg);
+	ret_ssi  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI,  "ssi",  conf_ssi);
 	if (ret_ssiu < 0 ||
 	if (ret_ssiu < 0 ||
 	    ret_scu  < 0 ||
 	    ret_scu  < 0 ||
 	    ret_adg  < 0 ||
 	    ret_adg  < 0 ||
 	    ret_ssi  < 0)
 	    ret_ssi  < 0)
 		return ret_ssiu | ret_scu | ret_adg | ret_ssi;
 		return ret_ssiu | ret_scu | ret_adg | ret_ssi;
 
 
-	dev_dbg(dev, "Gen2 is probed\n");
-
 	return 0;
 	return 0;
 }
 }
 
 
@@ -390,7 +287,6 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
 static int rsnd_gen1_probe(struct platform_device *pdev,
 static int rsnd_gen1_probe(struct platform_device *pdev,
 			   struct rsnd_priv *priv)
 			   struct rsnd_priv *priv)
 {
 {
-	struct device *dev = rsnd_priv_to_dev(priv);
 	struct rsnd_regmap_field_conf conf_sru[] = {
 	struct rsnd_regmap_field_conf conf_sru[] = {
 		RSND_GEN_S_REG(SRC_ROUTE_SEL,	0x00),
 		RSND_GEN_S_REG(SRC_ROUTE_SEL,	0x00),
 		RSND_GEN_S_REG(SRC_TMG_SEL0,	0x08),
 		RSND_GEN_S_REG(SRC_TMG_SEL0,	0x08),
@@ -440,16 +336,14 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
 	int ret_adg;
 	int ret_adg;
 	int ret_ssi;
 	int ret_ssi;
 
 
-	ret_sru  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU,  conf_sru);
-	ret_adg  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG,  conf_adg);
-	ret_ssi  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI,  conf_ssi);
+	ret_sru  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, "sru", conf_sru);
+	ret_adg  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, "adg", conf_adg);
+	ret_ssi  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, "ssi", conf_ssi);
 	if (ret_sru  < 0 ||
 	if (ret_sru  < 0 ||
 	    ret_adg  < 0 ||
 	    ret_adg  < 0 ||
 	    ret_ssi  < 0)
 	    ret_ssi  < 0)
 		return ret_sru | ret_adg | ret_ssi;
 		return ret_sru | ret_adg | ret_ssi;
 
 
-	dev_dbg(dev, "Gen1 is probed\n");
-
 	return 0;
 	return 0;
 }
 }
 
 

+ 53 - 28
sound/soc/sh/rcar/rsnd.h

@@ -170,21 +170,47 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod);
 /*
 /*
  *	R-Car DMA
  *	R-Car DMA
  */
  */
-struct rsnd_dma {
-	struct sh_dmae_slave	slave;
+struct rsnd_dma;
+struct rsnd_dma_ops {
+	void (*start)(struct rsnd_dma *dma);
+	void (*stop)(struct rsnd_dma *dma);
+	int (*init)(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
+		    struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
+	void  (*quit)(struct rsnd_dma *dma);
+};
+
+struct rsnd_dmaen {
 	struct dma_chan		*chan;
 	struct dma_chan		*chan;
-	enum dma_transfer_direction dir;
-	dma_addr_t		addr;
 };
 };
 
 
+struct rsnd_dmapp {
+	int			dmapp_id;
+	u32			chcr;
+};
+
+struct rsnd_dma {
+	struct rsnd_dma_ops	*ops;
+	dma_addr_t		src_addr;
+	dma_addr_t		dst_addr;
+	union {
+		struct rsnd_dmaen en;
+		struct rsnd_dmapp pp;
+	} dma;
+};
+#define rsnd_dma_to_dmaen(dma)	(&(dma)->dma.en)
+#define rsnd_dma_to_dmapp(dma)	(&(dma)->dma.pp)
+
 void rsnd_dma_start(struct rsnd_dma *dma);
 void rsnd_dma_start(struct rsnd_dma *dma);
 void rsnd_dma_stop(struct rsnd_dma *dma);
 void rsnd_dma_stop(struct rsnd_dma *dma);
-int rsnd_dma_available(struct rsnd_dma *dma);
-int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
-	int is_play, int id);
-void  rsnd_dma_quit(struct rsnd_priv *priv,
-		    struct rsnd_dma *dma);
+int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id);
+void  rsnd_dma_quit(struct rsnd_dma *dma);
+int rsnd_dma_probe(struct platform_device *pdev,
+		   const struct rsnd_of_data *of_data,
+		   struct rsnd_priv *priv);
+struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
+					  struct rsnd_mod *mod, char *name);
 
 
+#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
 
 
 /*
 /*
  *	R-Car sound mod
  *	R-Car sound mod
@@ -198,7 +224,7 @@ enum rsnd_mod_type {
 
 
 struct rsnd_mod_ops {
 struct rsnd_mod_ops {
 	char *name;
 	char *name;
-	char* (*dma_name)(struct rsnd_mod *mod);
+	struct dma_chan* (*dma_req)(struct rsnd_mod *mod);
 	int (*probe)(struct rsnd_mod *mod,
 	int (*probe)(struct rsnd_mod *mod,
 		     struct rsnd_priv *priv);
 		     struct rsnd_priv *priv);
 	int (*remove)(struct rsnd_mod *mod,
 	int (*remove)(struct rsnd_mod *mod,
@@ -213,6 +239,9 @@ struct rsnd_mod_ops {
 		    struct rsnd_priv *priv);
 		    struct rsnd_priv *priv);
 	int (*pcm_new)(struct rsnd_mod *mod,
 	int (*pcm_new)(struct rsnd_mod *mod,
 		       struct snd_soc_pcm_runtime *rtd);
 		       struct snd_soc_pcm_runtime *rtd);
+	int (*hw_params)(struct rsnd_mod *mod,
+			 struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *hw_params);
 	int (*fallback)(struct rsnd_mod *mod,
 	int (*fallback)(struct rsnd_mod *mod,
 			struct rsnd_priv *priv);
 			struct rsnd_priv *priv);
 };
 };
@@ -236,6 +265,9 @@ struct rsnd_mod {
  * 2	0: start	1: stop
  * 2	0: start	1: stop
  * 3	0: pcm_new
  * 3	0: pcm_new
  * 4	0: fallback
  * 4	0: fallback
+ *
+ * 31 bit is always called (see __rsnd_mod_call)
+ * 31	0: hw_params
  */
  */
 #define __rsnd_mod_shift_probe		0
 #define __rsnd_mod_shift_probe		0
 #define __rsnd_mod_shift_remove		0
 #define __rsnd_mod_shift_remove		0
@@ -245,6 +277,7 @@ struct rsnd_mod {
 #define __rsnd_mod_shift_stop		2
 #define __rsnd_mod_shift_stop		2
 #define __rsnd_mod_shift_pcm_new	3
 #define __rsnd_mod_shift_pcm_new	3
 #define __rsnd_mod_shift_fallback	4
 #define __rsnd_mod_shift_fallback	4
+#define __rsnd_mod_shift_hw_params	31 /* always called */
 
 
 #define __rsnd_mod_call_probe		0
 #define __rsnd_mod_call_probe		0
 #define __rsnd_mod_call_remove		1
 #define __rsnd_mod_call_remove		1
@@ -254,10 +287,10 @@ struct rsnd_mod {
 #define __rsnd_mod_call_stop		1
 #define __rsnd_mod_call_stop		1
 #define __rsnd_mod_call_pcm_new		0
 #define __rsnd_mod_call_pcm_new		0
 #define __rsnd_mod_call_fallback	0
 #define __rsnd_mod_call_fallback	0
+#define __rsnd_mod_call_hw_params	0
 
 
 #define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod)))
 #define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod)))
 #define rsnd_mod_to_dma(mod) (&(mod)->dma)
 #define rsnd_mod_to_dma(mod) (&(mod)->dma)
-#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
 #define rsnd_mod_to_io(mod) ((mod)->io)
 #define rsnd_mod_to_io(mod) ((mod)->io)
 #define rsnd_mod_id(mod) ((mod)->id)
 #define rsnd_mod_id(mod) ((mod)->id)
 #define rsnd_mod_hw_start(mod)	clk_enable((mod)->clk)
 #define rsnd_mod_hw_start(mod)	clk_enable((mod)->clk)
@@ -270,13 +303,14 @@ 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);
-char *rsnd_mod_dma_name(struct rsnd_mod *mod);
+struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod);
 
 
 /*
 /*
  *	R-Car sound DAI
  *	R-Car sound DAI
  */
  */
 #define RSND_DAI_NAME_SIZE	16
 #define RSND_DAI_NAME_SIZE	16
 struct rsnd_dai_stream {
 struct rsnd_dai_stream {
+	char name[RSND_DAI_NAME_SIZE];
 	struct snd_pcm_substream *substream;
 	struct snd_pcm_substream *substream;
 	struct rsnd_mod *mod[RSND_MOD_MAX];
 	struct rsnd_mod *mod[RSND_MOD_MAX];
 	struct rsnd_dai_path_info *info; /* rcar_snd.h */
 	struct rsnd_dai_path_info *info; /* rcar_snd.h */
@@ -332,9 +366,7 @@ int rsnd_gen_probe(struct platform_device *pdev,
 void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
 void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
 			       struct rsnd_mod *mod,
 			       struct rsnd_mod *mod,
 			       enum rsnd_reg reg);
 			       enum rsnd_reg reg);
-dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv,
-		       struct rsnd_mod *mod,
-		       int is_play,  int is_from);
+phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id);
 
 
 #define rsnd_is_gen1(s)		(((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1)
 #define rsnd_is_gen1(s)		(((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1)
 #define rsnd_is_gen2(s)		(((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2)
 #define rsnd_is_gen2(s)		(((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2)
@@ -389,6 +421,11 @@ struct rsnd_priv {
 	 */
 	 */
 	void *adg;
 	void *adg;
 
 
+	/*
+	 * below value will be filled on rsnd_dma_probe()
+	 */
+	void *dma;
+
 	/*
 	/*
 	 * below value will be filled on rsnd_ssi_probe()
 	 * below value will be filled on rsnd_ssi_probe()
 	 */
 	 */
@@ -415,19 +452,6 @@ struct rsnd_priv {
 #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
 #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
 #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)
 #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)
 
 
-#define rsnd_info_is_playback(priv, type)				\
-({									\
-	struct rcar_snd_info *info = rsnd_priv_to_info(priv);		\
-	int i, is_play = 0;						\
-	for (i = 0; i < info->dai_info_nr; i++) {			\
-		if (info->dai_info[i].playback.type == (type)->info) {	\
-			is_play = 1;					\
-			break;						\
-		}							\
-	}								\
-	is_play;							\
-})
-
 /*
 /*
  *	rsnd_kctrl
  *	rsnd_kctrl
  */
  */
@@ -506,6 +530,7 @@ void rsnd_ssi_remove(struct platform_device *pdev,
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
 int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
 int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
 int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
 int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
+int rsnd_ssi_use_busif(struct rsnd_mod *mod);
 
 
 /*
 /*
  *	R-Car DVC
  *	R-Car DVC

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

@@ -0,0 +1,512 @@
+/*
+ * Renesas Sampling Rate Convert Sound Card for DPCM
+ *
+ * Copyright (C) 2015 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * based on ${LINUX}/sound/soc/generic/simple-card.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+struct rsrc_card_of_data {
+	const char *prefix;
+	const struct snd_soc_dapm_route *routes;
+	int num_routes;
+};
+
+static const struct snd_soc_dapm_route routes_ssi0_ak4642[] = {
+	{"ak4642 Playback", NULL, "DAI0 Playback"},
+	{"DAI0 Capture", NULL, "ak4642 Capture"},
+};
+
+static const struct rsrc_card_of_data routes_of_ssi0_ak4642 = {
+	.prefix		= "ak4642",
+	.routes		= routes_ssi0_ak4642,
+	.num_routes	= ARRAY_SIZE(routes_ssi0_ak4642),
+};
+
+static const struct of_device_id rsrc_card_of_match[] = {
+	{ .compatible = "renesas,rsrc-card,lager",	.data = &routes_of_ssi0_ak4642 },
+	{ .compatible = "renesas,rsrc-card,koelsch",	.data = &routes_of_ssi0_ak4642 },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rsrc_card_of_match);
+
+struct rsrc_card_dai {
+	const char *name;
+	unsigned int fmt;
+	unsigned int sysclk;
+	struct clk *clk;
+};
+
+#define RSRC_FB_NUM	2 /* FE/BE */
+#define IDX_CPU		0
+#define IDX_CODEC	1
+struct rsrc_card_priv {
+	struct snd_soc_card snd_card;
+	struct rsrc_card_dai_props {
+		struct rsrc_card_dai cpu_dai;
+		struct rsrc_card_dai codec_dai;
+	} dai_props[RSRC_FB_NUM];
+	struct snd_soc_codec_conf codec_conf;
+	struct snd_soc_dai_link dai_link[RSRC_FB_NUM];
+	u32 convert_rate;
+};
+
+#define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev)
+#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
+#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + i)
+#define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data)
+
+static int rsrc_card_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct rsrc_card_priv *priv =	snd_soc_card_get_drvdata(rtd->card);
+	struct rsrc_card_dai_props *dai_props =
+		&priv->dai_props[rtd - rtd->card->rtd];
+	int ret;
+
+	ret = clk_prepare_enable(dai_props->cpu_dai.clk);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(dai_props->codec_dai.clk);
+	if (ret)
+		clk_disable_unprepare(dai_props->cpu_dai.clk);
+
+	return ret;
+}
+
+static void rsrc_card_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct rsrc_card_priv *priv =	snd_soc_card_get_drvdata(rtd->card);
+	struct rsrc_card_dai_props *dai_props =
+		&priv->dai_props[rtd - rtd->card->rtd];
+
+	clk_disable_unprepare(dai_props->cpu_dai.clk);
+
+	clk_disable_unprepare(dai_props->codec_dai.clk);
+}
+
+static struct snd_soc_ops rsrc_card_ops = {
+	.startup = rsrc_card_startup,
+	.shutdown = rsrc_card_shutdown,
+};
+
+static int __rsrc_card_dai_init(struct snd_soc_dai *dai,
+				struct rsrc_card_dai *set)
+{
+	int ret;
+
+	if (set->fmt) {
+		ret = snd_soc_dai_set_fmt(dai, set->fmt);
+		if (ret && ret != -ENOTSUPP) {
+			dev_err(dai->dev, "set_fmt error\n");
+			goto err;
+		}
+	}
+
+	if (set->sysclk) {
+		ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
+		if (ret && ret != -ENOTSUPP) {
+			dev_err(dai->dev, "set_sysclk error\n");
+			goto err;
+		}
+	}
+
+	ret = 0;
+
+err:
+	return ret;
+}
+
+static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct rsrc_card_priv *priv =	snd_soc_card_get_drvdata(rtd->card);
+	struct snd_soc_dai *codec = rtd->codec_dai;
+	struct snd_soc_dai *cpu = rtd->cpu_dai;
+	struct rsrc_card_dai_props *dai_props;
+	int num, ret;
+
+	num = rtd - rtd->card->rtd;
+	dai_props = &priv->dai_props[num];
+	ret = __rsrc_card_dai_init(codec, &dai_props->codec_dai);
+	if (ret < 0)
+		return ret;
+
+	ret = __rsrc_card_dai_init(cpu, &dai_props->cpu_dai);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					struct snd_pcm_hw_params *params)
+{
+	struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+	struct snd_interval *rate = hw_param_interval(params,
+						      SNDRV_PCM_HW_PARAM_RATE);
+
+	if (!priv->convert_rate)
+		return 0;
+
+	rate->min = rate->max = priv->convert_rate;
+
+	return 0;
+}
+
+static int
+rsrc_card_sub_parse_of(struct rsrc_card_priv *priv,
+		       struct device_node *np,
+		       struct rsrc_card_dai *dai,
+		       struct snd_soc_dai_link *dai_link,
+		       int *args_count)
+{
+	struct device *dev = rsrc_priv_to_dev(priv);
+	const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
+	struct of_phandle_args args;
+	struct device_node **p_node;
+	struct clk *clk;
+	const char **dai_name;
+	const char **name;
+	u32 val;
+	int ret;
+
+	if (args_count) {
+		p_node		= &dai_link->cpu_of_node;
+		dai_name	= &dai_link->cpu_dai_name;
+		name		= &dai_link->cpu_name;
+	} else {
+		p_node		= &dai_link->codec_of_node;
+		dai_name	= &dai_link->codec_dai_name;
+		name		= &dai_link->codec_name;
+	}
+
+	if (!np) {
+		/* use snd-soc-dummy */
+		*p_node		= NULL;
+		*dai_name	= "snd-soc-dummy-dai";
+		*name		= "snd-soc-dummy";
+		return 0;
+	}
+
+	/*
+	 * Get node via "sound-dai = <&phandle port>"
+	 * it will be used as xxx_of_node on soc_bind_dai_link()
+	 */
+	ret = of_parse_phandle_with_args(np, "sound-dai",
+					 "#sound-dai-cells", 0, &args);
+	if (ret)
+		return ret;
+
+	*p_node = args.np;
+
+	/* Get dai->name */
+	ret = snd_soc_of_get_dai_name(np, dai_name);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * FIXME
+	 *
+	 * rsrc assumes DPCM playback/capture
+	 */
+	dai_link->dpcm_playback = 1;
+	dai_link->dpcm_capture = 1;
+
+	if (args_count) {
+		*args_count = args.args_count;
+		dai_link->dynamic = 1;
+	} else {
+		dai_link->no_pcm = 1;
+		priv->codec_conf.of_node = (*p_node);
+		priv->codec_conf.name_prefix = of_data->prefix;
+	}
+
+	/*
+	 * Parse dai->sysclk come from "clocks = <&xxx>"
+	 * (if system has common clock)
+	 *  or "system-clock-frequency = <xxx>"
+	 *  or device's module clock.
+	 */
+	if (of_property_read_bool(np, "clocks")) {
+		clk = of_clk_get(np, 0);
+		if (IS_ERR(clk)) {
+			ret = PTR_ERR(clk);
+			return ret;
+		}
+
+		dai->sysclk = clk_get_rate(clk);
+		dai->clk = clk;
+	} else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
+		dai->sysclk = val;
+	} else {
+		clk = of_clk_get(args.np, 0);
+		if (!IS_ERR(clk))
+			dai->sysclk = clk_get_rate(clk);
+	}
+
+	return 0;
+}
+
+static int rsrc_card_parse_daifmt(struct device_node *node,
+				  struct rsrc_card_priv *priv,
+				  struct device_node *codec,
+				  int idx)
+{
+	struct device_node *bitclkmaster = NULL;
+	struct device_node *framemaster = NULL;
+	struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx);
+	struct rsrc_card_dai *cpu_dai = &dai_props->cpu_dai;
+	struct rsrc_card_dai *codec_dai = &dai_props->codec_dai;
+	unsigned int daifmt;
+
+	daifmt = snd_soc_of_parse_daifmt(node, NULL,
+					 &bitclkmaster, &framemaster);
+	daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+
+	if (!bitclkmaster && !framemaster)
+		return -EINVAL;
+
+	if (codec == bitclkmaster)
+		daifmt |= (codec == framemaster) ?
+			SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
+	else
+		daifmt |= (codec == framemaster) ?
+			SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
+
+	cpu_dai->fmt	= daifmt;
+	codec_dai->fmt	= daifmt;
+
+	of_node_put(bitclkmaster);
+	of_node_put(framemaster);
+
+	return 0;
+}
+
+static int rsrc_card_dai_link_of(struct device_node *node,
+				 struct rsrc_card_priv *priv,
+				 int idx)
+{
+	struct device *dev = rsrc_priv_to_dev(priv);
+	struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
+	struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx);
+	struct device_node *cpu = NULL;
+	struct device_node *codec = NULL;
+	char *name;
+	char prop[128];
+	int ret, cpu_args;
+
+	cpu = of_get_child_by_name(node, "cpu");
+	codec = of_get_child_by_name(node, "codec");
+
+	if (!cpu || !codec) {
+		ret = -EINVAL;
+		dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
+		goto dai_link_of_err;
+	}
+
+	ret = rsrc_card_parse_daifmt(node, priv, codec, idx);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CPU) ? cpu : NULL,
+				     &dai_props->cpu_dai,
+				     dai_link,
+				     &cpu_args);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CODEC) ? codec : NULL,
+				     &dai_props->codec_dai,
+				     dai_link,
+				     NULL);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
+		ret = -EINVAL;
+		goto dai_link_of_err;
+	}
+
+	/* Simple Card assumes platform == cpu */
+	dai_link->platform_of_node = dai_link->cpu_of_node;
+
+	/* DAI link name is created from CPU/CODEC dai name */
+	name = devm_kzalloc(dev,
+			    strlen(dai_link->cpu_dai_name)   +
+			    strlen(dai_link->codec_dai_name) + 2,
+			    GFP_KERNEL);
+	if (!name) {
+		ret = -ENOMEM;
+		goto dai_link_of_err;
+	}
+
+	sprintf(name, "%s-%s", dai_link->cpu_dai_name,
+		dai_link->codec_dai_name);
+	dai_link->name = dai_link->stream_name = name;
+	dai_link->ops = &rsrc_card_ops;
+	dai_link->init = rsrc_card_dai_init;
+
+	if (idx == IDX_CODEC)
+		dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup;
+
+	dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
+	dev_dbg(dev, "\tcpu : %s / %04x / %d\n",
+		dai_link->cpu_dai_name,
+		dai_props->cpu_dai.fmt,
+		dai_props->cpu_dai.sysclk);
+	dev_dbg(dev, "\tcodec : %s / %04x / %d\n",
+		dai_link->codec_dai_name,
+		dai_props->codec_dai.fmt,
+		dai_props->codec_dai.sysclk);
+
+	/*
+	 * In soc_bind_dai_link() will check cpu name after
+	 * of_node matching if dai_link has cpu_dai_name.
+	 * but, it will never match if name was created by
+	 * fmt_single_name() remove cpu_dai_name if cpu_args
+	 * was 0. See:
+	 *	fmt_single_name()
+	 *	fmt_multiple_name()
+	 */
+	if (!cpu_args)
+		dai_link->cpu_dai_name = NULL;
+
+dai_link_of_err:
+	of_node_put(cpu);
+	of_node_put(codec);
+
+	return ret;
+}
+
+static int rsrc_card_parse_of(struct device_node *node,
+			      struct rsrc_card_priv *priv)
+{
+	struct device *dev = rsrc_priv_to_dev(priv);
+	const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
+	int ret;
+	int i;
+
+	if (!node)
+		return -EINVAL;
+
+	/* Parse the card name from DT */
+	snd_soc_of_parse_card_name(&priv->snd_card, "card-name");
+
+	/* DAPM routes */
+	priv->snd_card.of_dapm_routes		= of_data->routes;
+	priv->snd_card.num_of_dapm_routes	= of_data->num_routes;
+
+	/* sampling rate convert */
+	of_property_read_u32(node, "convert-rate", &priv->convert_rate);
+
+	dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n",
+		priv->snd_card.name ? priv->snd_card.name : "",
+		priv->convert_rate);
+
+	/* FE/BE */
+	for (i = 0; i < RSRC_FB_NUM; i++) {
+		ret = rsrc_card_dai_link_of(node, priv, i);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (!priv->snd_card.name)
+		priv->snd_card.name = priv->snd_card.dai_link->name;
+
+	return 0;
+}
+
+/* Decrease the reference count of the device nodes */
+static int rsrc_card_unref(struct snd_soc_card *card)
+{
+	struct snd_soc_dai_link *dai_link;
+	int num_links;
+
+	for (num_links = 0, dai_link = card->dai_link;
+	     num_links < card->num_links;
+	     num_links++, dai_link++) {
+		of_node_put(dai_link->cpu_of_node);
+		of_node_put(dai_link->codec_of_node);
+	}
+	return 0;
+}
+
+static int rsrc_card_probe(struct platform_device *pdev)
+{
+	struct rsrc_card_priv *priv;
+	struct snd_soc_dai_link *dai_link;
+	struct device_node *np = pdev->dev.of_node;
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	/* Allocate the private data */
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	/* Init snd_soc_card */
+	priv->snd_card.owner = THIS_MODULE;
+	priv->snd_card.dev = dev;
+	dai_link = priv->dai_link;
+	priv->snd_card.dai_link = dai_link;
+	priv->snd_card.num_links = RSRC_FB_NUM;
+	priv->snd_card.codec_conf = &priv->codec_conf;
+	priv->snd_card.num_configs = 1;
+
+	ret = rsrc_card_parse_of(np, priv);
+	if (ret < 0) {
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "parse error %d\n", ret);
+		goto err;
+	}
+
+	snd_soc_card_set_drvdata(&priv->snd_card, priv);
+
+	ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
+	if (ret >= 0)
+		return ret;
+err:
+	rsrc_card_unref(&priv->snd_card);
+
+	return ret;
+}
+
+static int rsrc_card_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	return rsrc_card_unref(card);
+}
+
+static struct platform_driver rsrc_card = {
+	.driver = {
+		.name = "renesas-src-audio-card",
+		.of_match_table = rsrc_card_of_match,
+	},
+	.probe = rsrc_card_probe,
+	.remove = rsrc_card_remove,
+};
+
+module_platform_driver(rsrc_card);
+
+MODULE_ALIAS("platform:renesas-src-audio-card");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas Sampling Rate Convert Sound Card");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");

+ 191 - 42
sound/soc/sh/rcar/src.c

@@ -22,16 +22,20 @@
 struct rsnd_src {
 struct rsnd_src {
 	struct rsnd_src_platform_info *info; /* rcar_snd.h */
 	struct rsnd_src_platform_info *info; /* rcar_snd.h */
 	struct rsnd_mod mod;
 	struct rsnd_mod mod;
+	struct rsnd_kctrl_cfg_s sen;  /* sync convert enable */
+	struct rsnd_kctrl_cfg_s sync; /* sync convert */
+	u32 convert_rate; /* sampling rate convert */
 	int err;
 	int err;
 };
 };
 
 
 #define RSND_SRC_NAME_SIZE 16
 #define RSND_SRC_NAME_SIZE 16
 
 
-#define rsnd_src_convert_rate(p) ((p)->info->convert_rate)
+#define rsnd_enable_sync_convert(src) ((src)->sen.val)
+#define rsnd_src_of_node(priv) \
+	of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
+
 #define rsnd_mod_to_src(_mod)				\
 #define rsnd_mod_to_src(_mod)				\
 	container_of((_mod), struct rsnd_src, mod)
 	container_of((_mod), struct rsnd_src, mod)
-#define rsnd_src_dma_available(src) \
-	rsnd_dma_available(rsnd_mod_to_dma(&(src)->mod))
 
 
 #define for_each_rsnd_src(pos, priv, i)				\
 #define for_each_rsnd_src(pos, priv, i)				\
 	for ((i) = 0;						\
 	for ((i) = 0;						\
@@ -113,6 +117,17 @@ struct rsnd_src {
 /*
 /*
  *		Gen1/Gen2 common functions
  *		Gen1/Gen2 common functions
  */
  */
+static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	int is_play = rsnd_io_is_play(io);
+
+	return rsnd_dma_request_channel(rsnd_src_of_node(priv),
+					mod,
+					is_play ? "rx" : "tx");
+}
+
 int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
 int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
 			int use_busif)
 			int use_busif)
 {
 {
@@ -220,6 +235,30 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod)
 	return 0;
 	return 0;
 }
 }
 
 
+static u32 rsnd_src_convert_rate(struct rsnd_src *src)
+{
+	struct rsnd_mod *mod = &src->mod;
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	u32 convert_rate;
+
+	if (!runtime)
+		return 0;
+
+	if (!rsnd_enable_sync_convert(src))
+		return src->convert_rate;
+
+	convert_rate = src->sync.val;
+
+	if (!convert_rate)
+		convert_rate = src->convert_rate;
+
+	if (!convert_rate)
+		convert_rate = runtime->rate;
+
+	return convert_rate;
+}
+
 unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
 unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
 				   struct rsnd_dai_stream *io,
 				   struct rsnd_dai_stream *io,
 				   struct snd_pcm_runtime *runtime)
 				   struct snd_pcm_runtime *runtime)
@@ -276,7 +315,43 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod)
 	return 0;
 	return 0;
 }
 }
 
 
-static int rsnd_src_init(struct rsnd_mod *mod)
+static int rsnd_src_hw_params(struct rsnd_mod *mod,
+			      struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *fe_params)
+{
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+	struct snd_soc_pcm_runtime *fe = substream->private_data;
+
+	/* default value (mainly for non-DT) */
+	src->convert_rate = src->info->convert_rate;
+
+	/*
+	 * SRC assumes that it is used under DPCM if user want to use
+	 * sampling rate convert. Then, SRC should be FE.
+	 * And then, this function will be called *after* BE settings.
+	 * this means, each BE already has fixuped hw_params.
+	 * see
+	 *	dpcm_fe_dai_hw_params()
+	 *	dpcm_be_dai_hw_params()
+	 */
+	if (fe->dai_link->dynamic) {
+		int stream = substream->stream;
+		struct snd_soc_dpcm *dpcm;
+		struct snd_pcm_hw_params *be_params;
+
+		list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+			be_params = &dpcm->hw_params;
+
+			if (params_rate(fe_params) != params_rate(be_params))
+				src->convert_rate = params_rate(be_params);
+		}
+	}
+
+	return 0;
+}
+
+static int rsnd_src_init(struct rsnd_mod *mod,
+			 struct rsnd_priv *priv)
 {
 {
 	struct rsnd_src *src = rsnd_mod_to_src(mod);
 	struct rsnd_src *src = rsnd_mod_to_src(mod);
 
 
@@ -284,6 +359,9 @@ static int rsnd_src_init(struct rsnd_mod *mod)
 
 
 	src->err = 0;
 	src->err = 0;
 
 
+	/* reset sync convert_rate */
+	src->sync.val = 0;
+
 	/*
 	/*
 	 * Initialize the operation of the SRC internal circuits
 	 * Initialize the operation of the SRC internal circuits
 	 * see rsnd_src_start()
 	 * see rsnd_src_start()
@@ -305,6 +383,11 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
 		dev_warn(dev, "%s[%d] under/over flow err = %d\n",
 		dev_warn(dev, "%s[%d] under/over flow err = %d\n",
 			 rsnd_mod_name(mod), rsnd_mod_id(mod), src->err);
 			 rsnd_mod_name(mod), rsnd_mod_id(mod), src->err);
 
 
+	src->convert_rate = 0;
+
+	/* reset sync convert_rate */
+	src->sync.val = 0;
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -448,23 +531,12 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod)
 	return 0;
 	return 0;
 }
 }
 
 
-static int rsnd_src_probe_gen1(struct rsnd_mod *mod,
-			       struct rsnd_priv *priv)
-{
-	struct device *dev = rsnd_priv_to_dev(priv);
-
-	dev_dbg(dev, "%s[%d] (Gen1) is probed\n",
-		rsnd_mod_name(mod), rsnd_mod_id(mod));
-
-	return 0;
-}
-
 static int rsnd_src_init_gen1(struct rsnd_mod *mod,
 static int rsnd_src_init_gen1(struct rsnd_mod *mod,
 			      struct rsnd_priv *priv)
 			      struct rsnd_priv *priv)
 {
 {
 	int ret;
 	int ret;
 
 
-	ret = rsnd_src_init(mod);
+	ret = rsnd_src_init(mod, priv);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
@@ -505,11 +577,12 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
 
 
 static struct rsnd_mod_ops rsnd_src_gen1_ops = {
 static struct rsnd_mod_ops rsnd_src_gen1_ops = {
 	.name	= SRC_NAME,
 	.name	= SRC_NAME,
-	.probe	= rsnd_src_probe_gen1,
+	.dma_req = rsnd_src_dma_req,
 	.init	= rsnd_src_init_gen1,
 	.init	= rsnd_src_init_gen1,
 	.quit	= rsnd_src_quit,
 	.quit	= rsnd_src_quit,
 	.start	= rsnd_src_start_gen1,
 	.start	= rsnd_src_start_gen1,
 	.stop	= rsnd_src_stop_gen1,
 	.stop	= rsnd_src_stop_gen1,
+	.hw_params = rsnd_src_hw_params,
 };
 };
 
 
 /*
 /*
@@ -607,13 +680,17 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
 
 
 	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);
+		struct rsnd_src *src = rsnd_mod_to_src(mod);
 		struct device *dev = rsnd_priv_to_dev(priv);
 		struct device *dev = rsnd_priv_to_dev(priv);
 
 
-		_rsnd_src_stop_gen2(mod);
-		_rsnd_src_start_gen2(mod);
-
 		dev_dbg(dev, "%s[%d] restart\n",
 		dev_dbg(dev, "%s[%d] restart\n",
 			rsnd_mod_name(mod), rsnd_mod_id(mod));
 			rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+		_rsnd_src_stop_gen2(mod);
+		if (src->err < 1024)
+			_rsnd_src_start_gen2(mod);
+		else
+			dev_warn(dev, "no more SRC restart\n");
 	}
 	}
 
 
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
@@ -627,6 +704,7 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod)
 	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
 	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
 	struct rsnd_src *src = rsnd_mod_to_src(mod);
 	struct rsnd_src *src = rsnd_mod_to_src(mod);
 	u32 convert_rate = rsnd_src_convert_rate(src);
 	u32 convert_rate = rsnd_src_convert_rate(src);
+	u32 cr, route;
 	uint ratio;
 	uint ratio;
 	int ret;
 	int ret;
 
 
@@ -647,13 +725,21 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod)
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
-	rsnd_mod_write(mod, SRC_SRCCR, 0x00011110);
-
+	cr	= 0x00011110;
+	route	= 0x0;
 	if (convert_rate) {
 	if (convert_rate) {
-		/* Gen1/Gen2 are not compatible */
-		rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+		route	= 0x1;
+
+		if (rsnd_enable_sync_convert(src)) {
+			cr |= 0x1;
+			route |= rsnd_io_is_play(io) ?
+				(0x1 << 24) : (0x1 << 25);
+		}
 	}
 	}
 
 
+	rsnd_mod_write(mod, SRC_SRCCR, cr);
+	rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
+
 	switch (rsnd_mod_id(mod)) {
 	switch (rsnd_mod_id(mod)) {
 	case 5:
 	case 5:
 	case 6:
 	case 6:
@@ -708,24 +794,12 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
 				       IRQF_SHARED,
 				       IRQF_SHARED,
 				       dev_name(dev), mod);
 				       dev_name(dev), mod);
 		if (ret)
 		if (ret)
-			goto rsnd_src_probe_gen2_fail;
+			return ret;
 	}
 	}
 
 
 	ret = rsnd_dma_init(priv,
 	ret = rsnd_dma_init(priv,
 			    rsnd_mod_to_dma(mod),
 			    rsnd_mod_to_dma(mod),
-			    rsnd_info_is_playback(priv, src),
 			    src->info->dma_id);
 			    src->info->dma_id);
-	if (ret)
-		goto rsnd_src_probe_gen2_fail;
-
-	dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
-		rsnd_mod_name(mod), rsnd_mod_id(mod));
-
-	return ret;
-
-rsnd_src_probe_gen2_fail:
-	dev_err(dev, "%s[%d] (Gen2) failed\n",
-		rsnd_mod_name(mod), rsnd_mod_id(mod));
 
 
 	return ret;
 	return ret;
 }
 }
@@ -733,7 +807,7 @@ rsnd_src_probe_gen2_fail:
 static int rsnd_src_remove_gen2(struct rsnd_mod *mod,
 static int rsnd_src_remove_gen2(struct rsnd_mod *mod,
 				struct rsnd_priv *priv)
 				struct rsnd_priv *priv)
 {
 {
-	rsnd_dma_quit(priv, rsnd_mod_to_dma(mod));
+	rsnd_dma_quit(rsnd_mod_to_dma(mod));
 
 
 	return 0;
 	return 0;
 }
 }
@@ -743,7 +817,7 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod,
 {
 {
 	int ret;
 	int ret;
 
 
-	ret = rsnd_src_init(mod);
+	ret = rsnd_src_init(mod, priv);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
@@ -778,14 +852,91 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
 	return ret;
 	return ret;
 }
 }
 
 
+static void rsnd_src_reconvert_update(struct rsnd_mod *mod)
+{
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+	u32 convert_rate = rsnd_src_convert_rate(src);
+	u32 fsrate;
+
+	if (!runtime)
+		return;
+
+	if (!convert_rate)
+		convert_rate = runtime->rate;
+
+	fsrate = 0x0400000 / convert_rate * runtime->rate;
+
+	/* update IFS */
+	rsnd_mod_write(mod, SRC_IFSVR, fsrate);
+}
+
+static int rsnd_src_pcm_new(struct rsnd_mod *mod,
+			    struct snd_soc_pcm_runtime *rtd)
+{
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+	struct rsnd_src *src = rsnd_mod_to_src(mod);
+	int ret;
+
+	/*
+	 * enable SRC sync convert if possible
+	 */
+
+	/*
+	 * Gen1 is not supported
+	 */
+	if (rsnd_is_gen1(priv))
+		return 0;
+
+	/*
+	 * SRC sync convert needs clock master
+	 */
+	if (!rsnd_rdai_is_clk_master(rdai))
+		return 0;
+
+	/*
+	 * We can't use SRC sync convert
+	 * if it has DVC
+	 */
+	if (rsnd_io_to_mod_dvc(io))
+		return 0;
+
+	/*
+	 * enable sync convert
+	 */
+	ret = rsnd_kctrl_new_s(mod, rtd,
+			       rsnd_io_is_play(io) ?
+			       "SRC Out Rate Switch" :
+			       "SRC In Rate Switch",
+			       rsnd_src_reconvert_update,
+			       &src->sen, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = rsnd_kctrl_new_s(mod, rtd,
+			       rsnd_io_is_play(io) ?
+			       "SRC Out Rate" :
+			       "SRC In Rate",
+			       rsnd_src_reconvert_update,
+			       &src->sync, 192000);
+
+	return ret;
+}
+
 static struct rsnd_mod_ops rsnd_src_gen2_ops = {
 static struct rsnd_mod_ops rsnd_src_gen2_ops = {
 	.name	= SRC_NAME,
 	.name	= SRC_NAME,
+	.dma_req = rsnd_src_dma_req,
 	.probe	= rsnd_src_probe_gen2,
 	.probe	= rsnd_src_probe_gen2,
 	.remove	= rsnd_src_remove_gen2,
 	.remove	= rsnd_src_remove_gen2,
 	.init	= rsnd_src_init_gen2,
 	.init	= rsnd_src_init_gen2,
 	.quit	= rsnd_src_quit,
 	.quit	= rsnd_src_quit,
 	.start	= rsnd_src_start_gen2,
 	.start	= rsnd_src_start_gen2,
 	.stop	= rsnd_src_stop_gen2,
 	.stop	= rsnd_src_stop_gen2,
+	.hw_params = rsnd_src_hw_params,
+	.pcm_new = rsnd_src_pcm_new,
 };
 };
 
 
 struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
 struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
@@ -810,7 +961,7 @@ static void rsnd_of_parse_src(struct platform_device *pdev,
 	if (!of_data)
 	if (!of_data)
 		return;
 		return;
 
 
-	src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src");
+	src_node = rsnd_src_of_node(priv);
 	if (!src_node)
 	if (!src_node)
 		return;
 		return;
 
 
@@ -893,8 +1044,6 @@ int rsnd_src_probe(struct platform_device *pdev,
 		ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i);
 		ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i);
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
-
-		dev_dbg(dev, "SRC%d probed\n", i);
 	}
 	}
 
 
 	return 0;
 	return 0;

+ 26 - 30
sound/soc/sh/rcar/ssi.c

@@ -80,13 +80,13 @@ 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_dma_available(ssi) \
-	rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))
 #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
 #define rsnd_ssi_clk_from_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) \
+	of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
 
 
-static int rsnd_ssi_use_busif(struct rsnd_mod *mod)
+int rsnd_ssi_use_busif(struct rsnd_mod *mod)
 {
 {
 	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 	struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
 	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
@@ -416,11 +416,14 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
 		/*
 		/*
 		 * restart SSI
 		 * restart SSI
 		 */
 		 */
-		rsnd_ssi_stop(mod, priv);
-		rsnd_ssi_start(mod, priv);
-
 		dev_dbg(dev, "%s[%d] restart\n",
 		dev_dbg(dev, "%s[%d] restart\n",
 			rsnd_mod_name(mod), rsnd_mod_id(mod));
 			rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+		rsnd_ssi_stop(mod, priv);
+		if (ssi->err < 1024)
+			rsnd_ssi_start(mod, priv);
+		else
+			dev_warn(dev, "no more SSI restart\n");
 	}
 	}
 
 
 	rsnd_ssi_record_error(ssi, status);
 	rsnd_ssi_record_error(ssi, status);
@@ -442,12 +445,6 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
 			       rsnd_ssi_interrupt,
 			       rsnd_ssi_interrupt,
 			       IRQF_SHARED,
 			       IRQF_SHARED,
 			       dev_name(dev), ssi);
 			       dev_name(dev), ssi);
-	if (ret)
-		dev_err(dev, "%s[%d] (PIO) request interrupt failed\n",
-			rsnd_mod_name(mod), rsnd_mod_id(mod));
-	else
-		dev_dbg(dev, "%s[%d] (PIO) is probed\n",
-			rsnd_mod_name(mod), rsnd_mod_id(mod));
 
 
 	return ret;
 	return ret;
 }
 }
@@ -474,23 +471,11 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
 			       IRQF_SHARED,
 			       IRQF_SHARED,
 			       dev_name(dev), ssi);
 			       dev_name(dev), ssi);
 	if (ret)
 	if (ret)
-		goto rsnd_ssi_dma_probe_fail;
+		return ret;
 
 
 	ret = rsnd_dma_init(
 	ret = rsnd_dma_init(
 		priv, rsnd_mod_to_dma(mod),
 		priv, rsnd_mod_to_dma(mod),
-		rsnd_info_is_playback(priv, ssi),
 		dma_id);
 		dma_id);
-	if (ret)
-		goto rsnd_ssi_dma_probe_fail;
-
-	dev_dbg(dev, "%s[%d] (DMA) is probed\n",
-		rsnd_mod_name(mod), rsnd_mod_id(mod));
-
-	return ret;
-
-rsnd_ssi_dma_probe_fail:
-	dev_err(dev, "%s[%d] (DMA) is failed\n",
-		rsnd_mod_name(mod), rsnd_mod_id(mod));
 
 
 	return ret;
 	return ret;
 }
 }
@@ -502,7 +487,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
 	struct device *dev = rsnd_priv_to_dev(priv);
 	struct device *dev = rsnd_priv_to_dev(priv);
 	int irq = ssi->info->irq;
 	int irq = ssi->info->irq;
 
 
-	rsnd_dma_quit(priv, rsnd_mod_to_dma(mod));
+	rsnd_dma_quit(rsnd_mod_to_dma(mod));
 
 
 	/* PIO will request IRQ again */
 	/* PIO will request IRQ again */
 	devm_free_irq(dev, irq, ssi);
 	devm_free_irq(dev, irq, ssi);
@@ -554,14 +539,25 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
 	return 0;
 	return 0;
 }
 }
 
 
-static char *rsnd_ssi_dma_name(struct rsnd_mod *mod)
+static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_mod *mod)
 {
 {
-	return rsnd_ssi_use_busif(mod) ? "ssiu" : SSI_NAME;
+	struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+	struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+	int is_play = rsnd_io_is_play(io);
+	char *name;
+
+	if (rsnd_ssi_use_busif(mod))
+		name = is_play ? "rxu" : "txu";
+	else
+		name = is_play ? "rx" : "tx";
+
+	return rsnd_dma_request_channel(rsnd_ssi_of_node(priv),
+					mod, name);
 }
 }
 
 
 static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
 static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
 	.name	= SSI_NAME,
 	.name	= SSI_NAME,
-	.dma_name = rsnd_ssi_dma_name,
+	.dma_req = rsnd_ssi_dma_req,
 	.probe	= rsnd_ssi_dma_probe,
 	.probe	= rsnd_ssi_dma_probe,
 	.remove	= rsnd_ssi_dma_remove,
 	.remove	= rsnd_ssi_dma_remove,
 	.init	= rsnd_ssi_init,
 	.init	= rsnd_ssi_init,
@@ -636,7 +632,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
 	if (!of_data)
 	if (!of_data)
 		return;
 		return;
 
 
-	node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi");
+	node = rsnd_ssi_of_node(priv);
 	if (!node)
 	if (!node)
 		return;
 		return;