LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 2/8] net: ucc_geth: use platform_{get,set}_drvdata()
From: Libo Chen @ 2013-08-12 13:24 UTC (permalink / raw)
  To: leoli, David Miller; +Cc: netdev, Li Zefan, linuxppc-dev, LKML

Use the wrapper functions for getting and setting the driver data using
platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev,
so we can directly pass a struct platform_device.

Signed-off-by: Libo Chen <libo.chen@huawei.com>
---
 drivers/net/ethernet/freescale/ucc_geth.c |    4 +---
 1 files changed, 1 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c
index 3c43dac..5930c39 100644
--- a/drivers/net/ethernet/freescale/ucc_geth.c
+++ b/drivers/net/ethernet/freescale/ucc_geth.c
@@ -3911,14 +3911,12 @@ static int ucc_geth_probe(struct platform_device* ofdev)

 static int ucc_geth_remove(struct platform_device* ofdev)
 {
-	struct device *device = &ofdev->dev;
-	struct net_device *dev = dev_get_drvdata(device);
+	struct net_device *dev = platform_get_drvdata(ofdev);
 	struct ucc_geth_private *ugeth = netdev_priv(dev);

 	unregister_netdev(dev);
 	free_netdev(dev);
 	ucc_geth_memclean(ugeth);
-	dev_set_drvdata(device, NULL);

 	return 0;
 }
-- 
1.7.1

^ permalink raw reply related

* Re: [PATCH 2/2] Register bootmem pages at boot on powerpc
From: Nathan Fontenot @ 2013-08-12 13:01 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linux-mm, linuxppc-dev
In-Reply-To: <1376266763.32100.144.camel@pasglop>

On 08/11/2013 07:19 PM, Benjamin Herrenschmidt wrote:
> On Fri, 2013-08-09 at 10:32 -0500, Nathan Fontenot wrote:
> 
>> +void register_page_bootmem_memmap(unsigned long section_nr,
>> +				  struct page *start_page, unsigned long size)
>> +{
>> +	WARN_ONCE(1, KERN_INFO
>> +		  "Sparse Vmemmap not fully supported for bootmem info nodes\n");
>> +}
>>  #endif /* CONFIG_SPARSEMEM_VMEMMAP */
> 
> But SPARSEMEM_VMEMMAP is our default on ppc64 pseries ... and you are
> select'ing the new option, so it looks like we are missing something
> here...
> 
> Can you tell me a bit more, the above makes me nervous...

Ok, I agree. that message isn't quite right.

What I wanted to convey is that memory hotplug is not fully supported
on powerpc with SPARSE_VMEMMAP enabled.. Perhaps the message should read
"Memory hotplug is not fully supported for bootmem info nodes".

Thoughts?

-Nathan

> 
> Cheers,
> Ben.
> 
>> Index: powerpc/arch/powerpc/mm/mem.c
>> ===================================================================
>> --- powerpc.orig/arch/powerpc/mm/mem.c
>> +++ powerpc/arch/powerpc/mm/mem.c
>> @@ -297,12 +297,21 @@ void __init paging_init(void)
>>  }
>>  #endif /* ! CONFIG_NEED_MULTIPLE_NODES */
>>
>> +static void __init register_page_bootmem_info(void)
>> +{
>> +	int i;
>> +
>> +	for_each_online_node(i)
>> +		register_page_bootmem_info_node(NODE_DATA(i));
>> +}
>> +
>>  void __init mem_init(void)
>>  {
>>  #ifdef CONFIG_SWIOTLB
>>  	swiotlb_init(0);
>>  #endif
>>
>> +	register_page_bootmem_info();
>>  	high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);
>>  	set_max_mapnr(max_pfn);
>>  	free_all_bootmem();
>> Index: powerpc/mm/Kconfig
>> ===================================================================
>> --- powerpc.orig/mm/Kconfig
>> +++ powerpc/mm/Kconfig
>> @@ -183,7 +183,7 @@ config MEMORY_HOTPLUG_SPARSE
>>  config MEMORY_HOTREMOVE
>>  	bool "Allow for memory hot remove"
>>  	select MEMORY_ISOLATION
>> -	select HAVE_BOOTMEM_INFO_NODE if X86_64
>> +	select HAVE_BOOTMEM_INFO_NODE if (X86_64 || PPC64)
>>  	depends on MEMORY_HOTPLUG && ARCH_ENABLE_MEMORY_HOTREMOVE
>>  	depends on MIGRATION
>>
>>
>> _______________________________________________
>> Linuxppc-dev mailing list
>> Linuxppc-dev@lists.ozlabs.org
>> https://lists.ozlabs.org/listinfo/linuxppc-dev
> 
> 

^ permalink raw reply

* [PATCH] powerpc: Make chip-id information available to userspace
From: Vasant Hegde @ 2013-08-12 12:05 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Shivaprasad G Bhat

So far "/sys/devices/system/cpu/cpuX/topology/physical_package_id"
was always default (-1) on ppc64 architecture.

Now, some systems have an ibm,chip-id property in the cpu nodes in
the device tree. On these systems, we now use this information to
display physical_package_id.

Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
Signed-off-by: Shivaprasad G Bhat <sbhat@linux.vnet.ibm.com>
---
 arch/powerpc/include/asm/smp.h      |    1 +
 arch/powerpc/include/asm/topology.h |    1 +
 arch/powerpc/kernel/smp.c           |   15 +++++++++++++++
 3 files changed, 17 insertions(+)

diff --git a/arch/powerpc/include/asm/smp.h b/arch/powerpc/include/asm/smp.h
index ffbaabe..ff21fda 100644
--- a/arch/powerpc/include/asm/smp.h
+++ b/arch/powerpc/include/asm/smp.h
@@ -112,6 +112,7 @@ static inline struct cpumask *cpu_core_mask(int cpu)
 }
 
 extern int cpu_to_core_id(int cpu);
+extern int cpu_to_chip_id(int cpu);
 
 /* Since OpenPIC has only 4 IPIs, we use slightly different message numbers.
  *
diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h
index 161ab66..89e3ef2 100644
--- a/arch/powerpc/include/asm/topology.h
+++ b/arch/powerpc/include/asm/topology.h
@@ -96,6 +96,7 @@ static inline int prrn_is_enabled(void)
 #ifdef CONFIG_PPC64
 #include <asm/smp.h>
 
+#define topology_physical_package_id(cpu)	(cpu_to_chip_id(cpu))
 #define topology_thread_cpumask(cpu)	(per_cpu(cpu_sibling_map, cpu))
 #define topology_core_cpumask(cpu)	(per_cpu(cpu_core_map, cpu))
 #define topology_core_id(cpu)		(cpu_to_core_id(cpu))
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index 076977c..a59e1d3 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -574,6 +574,21 @@ out:
 	return id;
 }

+/* Return the value of the chip-id property corresponding
+ * to the given logical cpu.
+ */
+int cpu_to_chip_id(int cpu)
+{
+	struct device_node *np;
+
+	np = of_get_cpu_node(cpu, NULL);
+	if (!np)
+		return -1;
+
+	of_node_put(np);
+	return of_get_ibm_chip_id(np);
+}
+
 /* Helper routines for cpu to core mapping */
 int cpu_core_index_of_thread(int cpu)
 {

^ permalink raw reply related

* [PATCH v4 resent 2/2] ASoC: fsl: Add S/PDIF machine driver
From: Nicolin Chen @ 2013-08-12 12:05 UTC (permalink / raw)
  To: broonie, lars, p.zabel
  Cc: devicetree, alsa-devel, linuxppc-dev, timur, rob.herring
In-Reply-To: <cover.1376309076.git.b42378@freescale.com>

Add S/PDIF machine driver for Freescale i.MX series SoC.

Signed-off-by: Nicolin Chen <b42378@freescale.com>
---
 .../devicetree/bindings/sound/imx-audio-spdif.txt  |   29 +++++
 sound/soc/fsl/Kconfig                              |   11 ++
 sound/soc/fsl/Makefile                             |    2 +
 sound/soc/fsl/imx-spdif.c                          |  134 ++++++++++++++++++++
 4 files changed, 176 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
 create mode 100644 sound/soc/fsl/imx-spdif.c

diff --git a/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
new file mode 100644
index 0000000..9a3fa26
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
@@ -0,0 +1,29 @@
+Freescale i.MX audio complex with S/PDIF transceiver
+
+Required properties:
+
+  - compatible : "fsl,imx-audio-spdif"
+
+  - model : The user-visible name of this sound complex
+
+  - spdif-controller : The phandle of the i.MX S/PDIF controller
+
+
+Optional properties:
+
+  - spdif-transmitter : The phandle of the spdif-transmitter dummy codec
+
+  - spdif-receiver : The phandle of the spdif-receiver dummy codec
+
+* Note: At least one of these two properties should be set in the DT binding.
+
+
+Example:
+
+sound-spdif {
+	compatible = "fsl,imx-audio-spdif";
+	model = "imx-spdif";
+	spdif-controller = <&spdif>;
+	spdif-transmitter = <&spdif_tx_codec>;
+	spdif-receiver = <&spdif_rx_codec>;
+};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 2c518db..4cc118c 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -195,6 +195,17 @@ config SND_SOC_IMX_SGTL5000
 	  Say Y if you want to add support for SoC audio on an i.MX board with
 	  a sgtl5000 codec.
 
+config SND_SOC_IMX_SPDIF
+	tristate "SoC Audio support for i.MX boards with S/PDIF"
+	select SND_SOC_IMX_PCM_DMA
+	select SND_SOC_FSL_SPDIF
+	select SND_SOC_FSL_UTILS
+	select SND_SOC_SPDIF
+	help
+	  SoC Audio support for i.MX boards with S/PDIF
+	  Say Y if you want to add support for SoC audio on an i.MX board with
+	  a S/DPDIF.
+
 config SND_SOC_IMX_MC13783
 	tristate "SoC Audio support for I.MX boards with mc13783"
 	depends on MFD_MC13783 && ARM
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 4b5970e..e2aaff7 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -45,6 +45,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
 snd-soc-wm1133-ev1-objs := wm1133-ev1.o
 snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
 snd-soc-imx-wm8962-objs := imx-wm8962.o
+snd-soc-imx-spdif-objs :=imx-spdif.o
 snd-soc-imx-mc13783-objs := imx-mc13783.o
 
 obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
@@ -53,4 +54,5 @@ obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
 obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
 obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
 obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
+obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
 obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
new file mode 100644
index 0000000..893f3d1
--- /dev/null
+++ b/sound/soc/fsl/imx-spdif.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/soc.h>
+
+struct imx_spdif_data {
+	struct snd_soc_dai_link dai[2];
+	struct snd_soc_card card;
+};
+
+static int imx_spdif_audio_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *spdif_np, *codec_tx_np, *codec_rx_np;
+	struct platform_device *spdif_pdev;
+	struct imx_spdif_data *data;
+	int ret = 0, num_links = 0;
+
+	spdif_np = of_parse_phandle(np, "spdif-controller", 0);
+	if (!spdif_np) {
+		dev_err(&pdev->dev, "failed to find spdif-controller\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	spdif_pdev = of_find_device_by_node(spdif_np);
+	if (!spdif_pdev) {
+		dev_err(&pdev->dev, "failed to find S/PDIF device\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	codec_tx_np = of_parse_phandle(np, "spdif-transmitter", 0);
+	if (codec_tx_np) {
+		data->dai[num_links].name = "S/PDIF TX";
+		data->dai[num_links].stream_name = "S/PDIF PCM Playback";
+		data->dai[num_links].codec_dai_name = "dit-hifi";
+		data->dai[num_links].codec_of_node = codec_tx_np;
+		data->dai[num_links].cpu_of_node = spdif_np;
+		data->dai[num_links].platform_of_node = spdif_np;
+		num_links++;
+	}
+
+	codec_rx_np = of_parse_phandle(np, "spdif-receiver", 0);
+	if (codec_rx_np) {
+		data->dai[num_links].name = "S/PDIF RX";
+		data->dai[num_links].stream_name = "S/PDIF PCM Capture";
+		data->dai[num_links].codec_dai_name = "dir-hifi";
+		data->dai[num_links].codec_of_node = codec_rx_np;
+		data->dai[num_links].cpu_of_node = spdif_np;
+		data->dai[num_links].platform_of_node = spdif_np;
+		num_links++;
+	}
+
+	if (!num_links) {
+		dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n");
+		goto fail;
+	}
+
+	data->card.dev = &pdev->dev;
+	data->card.num_links = num_links;
+	data->card.dai_link = data->dai;
+
+	ret = snd_soc_of_parse_card_name(&data->card, "model");
+	if (ret)
+		goto fail;
+
+	ret = snd_soc_register_card(&data->card);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+		goto fail;
+	}
+
+	platform_set_drvdata(pdev, data);
+
+fail:
+	if (codec_tx_np)
+		of_node_put(codec_tx_np);
+	if (codec_rx_np)
+		of_node_put(codec_rx_np);
+	if (spdif_np)
+		of_node_put(spdif_np);
+
+	return ret;
+}
+
+static int imx_spdif_audio_remove(struct platform_device *pdev)
+{
+	struct imx_spdif_data *data = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_card(&data->card);
+
+	return 0;
+}
+
+static const struct of_device_id imx_spdif_dt_ids[] = {
+	{ .compatible = "fsl,imx-audio-spdif", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids);
+
+static struct platform_driver imx_spdif_driver = {
+	.driver = {
+		.name = "imx-spdif",
+		.owner = THIS_MODULE,
+		.of_match_table = imx_spdif_dt_ids,
+	},
+	.probe = imx_spdif_audio_probe,
+	.remove = imx_spdif_audio_remove,
+};
+
+module_platform_driver(imx_spdif_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale i.MX S/PDIF machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-spdif");
-- 
1.7.1

^ permalink raw reply related

* [PATCH v4 resent 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Nicolin Chen @ 2013-08-12 12:05 UTC (permalink / raw)
  To: broonie, lars, p.zabel
  Cc: devicetree, alsa-devel, linuxppc-dev, timur, rob.herring
In-Reply-To: <cover.1376309076.git.b42378@freescale.com>

This patch add S/PDIF controller driver for Freescale SoC.

Signed-off-by: Nicolin Chen <b42378@freescale.com>
---
 .../devicetree/bindings/sound/fsl,spdif.txt        |  100 ++
 sound/soc/fsl/Kconfig                              |    3 +
 sound/soc/fsl/Makefile                             |    2 +
 sound/soc/fsl/fsl_spdif.c                          | 1350 ++++++++++++++++++++
 sound/soc/fsl/fsl_spdif.h                          |  223 ++++
 5 files changed, 1678 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/fsl,spdif.txt
 create mode 100644 sound/soc/fsl/fsl_spdif.c
 create mode 100644 sound/soc/fsl/fsl_spdif.h

diff --git a/Documentation/devicetree/bindings/sound/fsl,spdif.txt b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
new file mode 100644
index 0000000..e95318e
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
@@ -0,0 +1,100 @@
+Freescale Sony/Philips Digital Interface Format (S/PDIF) Controller
+
+The Freescale S/PDIF audio block is a stereo transceiver that allows the
+processor to receive and transmit digital audio via an coaxial cable or
+a fibre cable.
+
+Required properties:
+
+  - compatible : Compatible list, contains "fsl,<chip>-spdif". Using general
+  "fsl,fsl-spdif" will get the default SoC type -- imx6q-spdif.
+
+  - reg : Offset and length of the register set for the device.
+
+  - interrupts : Contains spdif interrupt.
+
+  - dmas : Generic dma devicetree binding as described in
+  Documentation/devicetree/bindings/dma/dma.txt.
+
+  - dma-names : Two dmas have to be defined, "tx" and "rx".
+
+  - clocks : Contains an entry for each entry in clock-names.
+
+  - clock-names : Includes the following entries:
+	name		type		comments
+	"core"		Required	The core clock of spdif controller
+	"rx"		Optional	Rx clock source for spdif record.
+					If absent, will use core clock.
+	"tx"		Optional	Tx clock source for spdif playback.
+					If absent, will use core clock.
+	"tx-32000"	Optional	Tx clock source for 32000Hz sample rate
+					playback. If absent, will use tx clock.
+	"tx-44100"	Optional	Tx clock source for 44100Hz sample rate
+					playback. If absent, will use tx clock.
+	"tx-48000"	Optional	Tx clock source for 48000Hz sample rate
+					playback. If absent, will use tx clock.
+
+  - tx-clksrc-names : The names for all available clock sources for tx, which
+  is also being listed in SoC reference manual, ClkSrc_Sel bit of SPDIF_SRPC.
+  And the name list would be different between different SoC. Use 'null' for
+  those unlisted names, and the max number of tx-clksrc-names should be 8.
+
+  - rx-clksrc-names : The names for all available clock sources for rx, which
+  is also being listed in SoC reference manual, TxClk_Source bit of SPDIF_STC.
+  And the name list would be different between different SoC. Use 'null' for
+  those unlisted names, and the max number of rx-clksrc-names should be 16.
+
+Optional properties:
+
+  - rx-clksrc-lock: This is a boolean property. If present, ClkSrc_Sel bit
+  of SPDIF_SRPC would be set a clock source that cares DPLL locked condition.
+
+Example1:
+
+spdif: spdif@02004000 {
+	compatible = "fsl,imx6q-spdif";
+	reg = <0x02004000 0x4000>;
+	interrupts = <0 52 0x04>;
+	dmas = <&sdma 14 18 0>,
+	       <&sdma 15 18 0>;
+	dma-names = "rx", "tx";
+
+	clocks = <&clks 197>;
+	clock-names = "core";
+	rx-clksrc-lock;
+	rx-clksrc-names =
+		"lock.ext", "lock.spdif", "lock.asrc",
+		"lock.spdif_ext", "lock.esai", "ext",
+		"spdif", "asrc", "spdif_ext", "esai",
+		"lock.mlb", "lock.mlb_phy", "mlb",
+		"mlb_phy";
+	tx-clksrc-names =
+		"xtal", "spdif", "asrc", "spdif_ext",
+		"esai", "ipg", "mlb", "mlb_phy";
+
+	status = "okay";
+};
+
+Example2:
+
+spdif: spdif@02004000 {
+	compatible = "fsl,imx6sl-spdif";
+	reg = <0x02004000 0x4000>;
+	interrupts = <0 52 0x04>;
+	dmas = <&sdma 14 18 0>,
+	       <&sdma 15 18 0>;
+	dma-names = "rx", "tx";
+
+	clocks = <&clks 122>;
+	clock-names = "core";
+	rx-clksrc-lock;
+	rx-clksrc-names =
+		"lock.xtal", "lock.spdif", "null", "lock.spdif_ext",
+		"null", "xtal", "spdif", "null", "spdif_ext", "null",
+		"null", "null", "mlb";
+	tx-clksrc-names =
+		"xtal", "spdif", "null", "spdif_ext",
+		"null", "ipg";
+
+	status = "okay";
+};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index e15f771..2c518db 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,6 +1,9 @@
 config SND_SOC_FSL_SSI
 	tristate
 
+config SND_SOC_FSL_SPDIF
+	tristate
+
 config SND_SOC_FSL_UTILS
 	tristate
 
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index d4b4aa8..4b5970e 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -12,9 +12,11 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
 
 # Freescale PowerPC SSI/DMA Platform Support
 snd-soc-fsl-ssi-objs := fsl_ssi.o
+snd-soc-fsl-spdif-objs := fsl_spdif.o
 snd-soc-fsl-utils-objs := fsl_utils.o
 snd-soc-fsl-dma-objs := fsl_dma.o
 obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
+obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
 obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
 obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
 
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
new file mode 100644
index 0000000..c99f50d
--- /dev/null
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -0,0 +1,1350 @@
+/*
+ * Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * Based on stmp3xxx_spdif_dai.c
+ * Vladimir Barinov <vbarinov@embeddedalley.com>
+ * Copyright 2008 SigmaTel, Inc
+ * Copyright 2008 Embedded Alley Solutions, Inc
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program  is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/clk-private.h>
+#include <linux/regmap.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include <sound/asoundef.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "fsl_spdif.h"
+#include "imx-pcm.h"
+
+#define FSL_SPDIF_TXFIFO_WML      0x8
+#define FSL_SPDIF_RXFIFO_WML      0x8
+
+#define INTR_FOR_PLAYBACK (INT_TXFIFO_RESYNC)
+#define INTR_FOR_CAPTURE (INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL | INT_URX_OV|\
+		INT_QRX_FUL | INT_QRX_OV | INT_UQ_SYNC | INT_UQ_ERR |\
+		INT_RXFIFO_RESYNC | INT_LOSS_LOCK | INT_DPLL_LOCKED)
+
+#define SRPC_CLK_MAX 16
+#define STC_CLK_MAX 8
+static struct {
+	char name[16];
+	bool lock;
+}tx_clklist[SRPC_CLK_MAX], rx_clklist[STC_CLK_MAX];
+
+/*
+ * SPDIF control structure
+ * Defines channel status, subcode and Q sub
+ */
+struct spdif_mixer_control {
+	/* spinlock to access control data */
+	spinlock_t ctl_lock;
+
+	/* IEC958 channel tx status bit */
+	unsigned char ch_status[4];
+
+	/* User bits */
+	unsigned char subcode[2 * SPDIF_UBITS_SIZE];
+
+	/* Q subcode part of user bits */
+	unsigned char qsub[2 * SPDIF_QSUB_SIZE];
+
+	/* buffer ptrs for writer */
+	u32 upos;
+	u32 qpos;
+
+	/* ready buffer index of the two buffers */
+	u32 ready_buf;
+};
+
+struct fsl_spdif_priv {
+	struct spdif_mixer_control fsl_spdif_control;
+	struct snd_soc_dai_driver cpu_dai_drv;
+	struct platform_device *pdev;
+	struct regmap *regmap;
+	atomic_t dpll_locked;
+	u32 irq;
+	u8 rxclk_src;
+	u8 txclk_src[3];
+	u8 txclk_div[SPDIF_TXRATE_MAX];
+	struct clk *txclk[3];
+	struct clk *txclk_main;
+	struct clk *rxclk;
+	struct clk *coreclk;
+	struct snd_dmaengine_dai_dma_data dma_params_tx;
+	struct snd_dmaengine_dai_dma_data dma_params_rx;
+
+	/* The name space will be allocated dynamically */
+	char name[0];
+};
+
+
+#ifdef DEBUG
+static void dumpregs(struct fsl_spdif_priv *spdif_priv)
+{
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 val, i;
+	int ret;
+
+	/* Valid address set of SPDIF is {[0x0-0x38], 0x44, 0x50} */
+	for (i = 0 ; i <= REG_SPDIF_STC; i += 4) {
+		ret = regmap_read(regmap, REG_SPDIF_SCR + i, &val);
+		if (!ret)
+			dev_dbg(&pdev->dev, "REG 0x%02x = 0x%06x\n", i, val);
+	}
+}
+#else
+static void dumpregs(struct fsl_spdif_priv *spdif_priv) {}
+#endif
+
+
+/* DPLL locked and lock loss interrupt handler */
+static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv)
+{
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 locked;
+
+	regmap_read(regmap, REG_SPDIF_SRPC, &locked);
+	locked &= SRPC_DPLL_LOCKED;
+
+	dev_dbg(&pdev->dev, "isr: Rx dpll %s \n",
+			locked ? "locked" : "loss lock");
+
+	atomic_set(&spdif_priv->dpll_locked, locked ? 1 : 0);
+}
+
+/* Receiver found illegal symbol interrupt handler */
+static void spdif_irq_sym_error(struct fsl_spdif_priv *spdif_priv)
+{
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+
+	dev_dbg(&pdev->dev, "isr: receiver found illegal symbol\n");
+
+	if (!atomic_read(&spdif_priv->dpll_locked)) {
+		/* dpll unlocked seems no audio stream */
+		regmap_update_bits(regmap, REG_SPDIF_SIE, INT_SYM_ERR, 0);
+	}
+}
+
+/* U/Q Channel receive register full */
+static void spdif_irq_uqrx_full(struct fsl_spdif_priv *spdif_priv, char name)
+{
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 *pos, size, val, reg;
+
+	switch (name) {
+	case 'U':
+		pos = &ctrl->upos;
+		size = SPDIF_UBITS_SIZE;
+		reg = REG_SPDIF_SRU;
+		break;
+	case 'Q':
+		pos = &ctrl->qpos;
+		size = SPDIF_QSUB_SIZE;
+		reg = REG_SPDIF_SRQ;
+		break;
+	default:
+		return;
+	}
+
+	dev_dbg(&pdev->dev, "isr: %c Channel receive register full\n", name);
+
+	if (*pos >= size * 2) {
+		*pos = 0;
+	} else if (unlikely((*pos % size) + 3 > size)) {
+		dev_err(&pdev->dev, "User bit receivce buffer overflow\n");
+		return;
+	}
+
+	regmap_read(regmap, reg, &val);
+	ctrl->subcode[*pos++] = val >> 16;
+	ctrl->subcode[*pos++] = val >> 8;
+	ctrl->subcode[*pos++] = val;
+}
+
+/* U/Q Channel sync found */
+static void spdif_irq_uq_sync(struct fsl_spdif_priv *spdif_priv)
+{
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	struct platform_device *pdev = spdif_priv->pdev;
+
+	dev_dbg(&pdev->dev, "isr: U/Q Channel sync found\n");
+
+	/* U/Q buffer reset */
+	if (ctrl->qpos == 0)
+		return;
+
+	/* set ready to this buffer */
+	ctrl->ready_buf = (ctrl->qpos - 1) / SPDIF_QSUB_SIZE + 1;
+}
+
+/* U/Q Channel framing error */
+static void spdif_irq_uq_err(struct fsl_spdif_priv *spdif_priv)
+{
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 val;
+
+	dev_dbg(&pdev->dev, "isr: U/Q Channel framing error\n");
+
+	/* read U/Q data and do buffer reset */
+	regmap_read(regmap, REG_SPDIF_SRU, &val);
+	regmap_read(regmap, REG_SPDIF_SRQ, &val);
+
+	/* drop this U/Q buffer */
+	ctrl->ready_buf = 0;
+	ctrl->upos = 0;
+	ctrl->qpos = 0;
+}
+
+/* Get spdif interrupt status and clear the interrupt */
+static u32 spdif_intr_status_clear(struct fsl_spdif_priv *spdif_priv)
+{
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 val, val2;
+
+	regmap_read(regmap, REG_SPDIF_SIS, &val);
+	regmap_read(regmap, REG_SPDIF_SIE, &val2);
+
+	regmap_write(regmap, REG_SPDIF_SIC, val & val2);
+
+	return val;
+}
+
+static irqreturn_t spdif_isr(int irq, void *devid)
+{
+	struct fsl_spdif_priv *spdif_priv = (struct fsl_spdif_priv *)devid;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 sis;
+
+	sis = spdif_intr_status_clear(spdif_priv);
+
+	if (sis & INT_DPLL_LOCKED)
+		spdif_irq_dpll_lock(spdif_priv);
+
+	if (sis & INT_TXFIFO_UNOV)
+		dev_dbg(&pdev->dev, "isr: Tx FIFO under/overrun\n");
+
+	if (sis & INT_TXFIFO_RESYNC)
+		dev_dbg(&pdev->dev, "isr: Tx FIFO resync\n");
+
+	if (sis & INT_CNEW)
+		dev_dbg(&pdev->dev, "isr: cstatus new\n");
+
+	if (sis & INT_VAL_NOGOOD)
+		dev_dbg(&pdev->dev, "isr: validity flag no good\n");
+
+	if (sis & INT_SYM_ERR)
+		spdif_irq_sym_error(spdif_priv);
+
+	if (sis & INT_BIT_ERR)
+		dev_dbg(&pdev->dev, "isr: receiver found parity bit error\n");
+
+	if (sis & INT_URX_FUL)
+		spdif_irq_uqrx_full(spdif_priv, 'U');
+
+	if (sis & INT_URX_OV)
+		dev_dbg(&pdev->dev, "isr: U Channel receive register overrun\n");
+
+	if (sis & INT_QRX_FUL)
+		spdif_irq_uqrx_full(spdif_priv, 'Q');
+
+	if (sis & INT_QRX_OV)
+		dev_dbg(&pdev->dev, "isr: Q Channel receive register overrun\n");
+
+	if (sis & INT_UQ_SYNC)
+		spdif_irq_uq_sync(spdif_priv);
+
+	if (sis & INT_UQ_ERR)
+		spdif_irq_uq_err(spdif_priv);
+
+	if (sis & INT_RXFIFO_UNOV)
+		dev_dbg(&pdev->dev, "isr: Rx FIFO under/overrun\n");
+
+	if (sis & INT_RXFIFO_RESYNC)
+		dev_dbg(&pdev->dev, "isr: Rx FIFO resync\n");
+
+	if (sis & INT_LOSS_LOCK)
+		spdif_irq_dpll_lock(spdif_priv);
+
+	/* FIXME: Write Tx FIFO to clear TxEm */
+	if (sis & INT_TX_EM)
+		dev_dbg(&pdev->dev, "isr: Tx FIFO empty\n");
+
+	/* FIXME: Read Rx FIFO to clear RxFIFOFul */
+	if (sis & INT_RXFIFO_FUL)
+		dev_dbg(&pdev->dev, "isr: Rx FIFO full\n");
+
+	return IRQ_HANDLED;
+}
+
+static void spdif_softreset(struct fsl_spdif_priv *spdif_priv)
+{
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 val, cycle = 1000;
+
+	regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET);
+	regcache_sync(regmap);
+
+	/* RESET bit would be cleared after finishing its reset procedure */
+	do {
+		regmap_read(regmap, REG_SPDIF_SCR, &val);
+	} while ((val & SCR_SOFT_RESET) && cycle--);
+}
+
+static void spdif_set_cstatus(struct spdif_mixer_control *ctrl,
+				u8 mask, u8 cstatus)
+{
+	ctrl->ch_status[3] &= ~mask;
+	ctrl->ch_status[3] |= cstatus & mask;
+}
+
+static u8 reverse_bits(u8 input)
+{
+	u8 tmp = input;
+
+	tmp = ((tmp & 0b10101010) >> 1) | ((tmp << 1) & 0b10101010);
+	tmp = ((tmp & 0b11001100) >> 2) | ((tmp << 2) & 0b11001100);
+	tmp = ((tmp & 0b11110000) >> 4) | ((tmp << 4) & 0b11110000);
+
+	return tmp;
+}
+
+static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv)
+{
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 ch_status;
+
+	ch_status = (reverse_bits(ctrl->ch_status[0]) << 16) |
+		(reverse_bits(ctrl->ch_status[1]) << 8) |
+		reverse_bits(ctrl->ch_status[2]);
+	regmap_write(regmap, REG_SPDIF_STCSCH, ch_status);
+
+	dev_dbg(&pdev->dev, "STCSCH: 0x%06x\n", ch_status);
+
+	ch_status = reverse_bits(ctrl->ch_status[3]) << 16;
+	regmap_write(regmap, REG_SPDIF_STCSCL, ch_status);
+
+	dev_dbg(&pdev->dev, "STCSCL: 0x%06x\n", ch_status);
+}
+
+/* Set SPDIF PhaseConfig register for rx clock */
+static int spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv,
+				enum spdif_gainsel gainsel, int dpll_locked)
+{
+	enum spdif_rxclk_src clksrc = spdif_priv->rxclk_src;
+	struct regmap *regmap = spdif_priv->regmap;
+
+	if (clksrc >= SRPC_CLKSRC_MAX || gainsel >= GAINSEL_MULTI_MAX)
+		return -EINVAL;
+
+	if (!dpll_locked && rx_clklist[clksrc].lock)
+		clksrc += SRPC_CLKSRC_SEL_LOCKED;
+
+	regmap_update_bits(regmap, REG_SPDIF_SRPC,
+			SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
+			SRPC_CLKSRC_SEL_SET(clksrc) | SRPC_GAINSEL_SET(gainsel));
+
+	return 0;
+}
+
+static int spdif_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned long rate_actual;
+
+	rate_actual = clk_round_rate(clk, rate);
+	clk_set_rate(clk, rate_actual);
+
+	return 0;
+}
+
+static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
+				int sample_rate)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	unsigned long clk = -1, div = 1, csfs = 0;
+	u32 stc, mask, rate;
+
+	switch (sample_rate) {
+	case 32000:
+		rate = SPDIF_TXRATE_32000;
+		csfs = IEC958_AES3_CON_FS_32000;
+		break;
+	case 44100:
+		rate = SPDIF_TXRATE_44100;
+		csfs = IEC958_AES3_CON_FS_44100;
+		break;
+	case 48000:
+		rate = SPDIF_TXRATE_48000;
+		csfs = IEC958_AES3_CON_FS_48000;
+		break;
+	default:
+		dev_err(&pdev->dev, "unsupported samplerate %d\n", sample_rate);
+		return -EINVAL;
+	}
+
+	clk = spdif_priv->txclk_src[rate];
+	if (clk < 0) {
+		dev_err(&pdev->dev, "no defined %d clk src\n", sample_rate);
+		return -EINVAL;
+	}
+
+	div = spdif_priv->txclk_div[rate];
+	if (div == 0) {
+		dev_err(&pdev->dev, "tx clock source is dividing by zero\n");
+		return -EINVAL;
+	}
+	/*
+	 * The S/PDIF block needs a clock of 64 * fs * div.  The S/PDIF block
+	 * will divide by (div).  So request 64 * fs * (div+1) which will
+	 * get rounded.
+	 */
+	spdif_clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1));
+
+	dev_dbg(&pdev->dev, "expected clock rate = %d\n",
+			(int)(64 * sample_rate * div));
+	dev_dbg(&pdev->dev, "acutal clock rate = %d\n",
+			(int)clk_get_rate(spdif_priv->txclk[rate]));
+
+	/* set fs field in consumer channel status */
+	spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
+
+	/* select clock source and divisor */
+	stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div);
+	mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK;
+	regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
+
+	dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate);
+
+	return 0;
+}
+
+int fsl_spdif_startup(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *cpu_dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 scr, mask, i;
+
+	/* Reset module and interrupts only for first initialization */
+	if (!cpu_dai->active) {
+		spdif_softreset(spdif_priv);
+
+		/* disable all the interrupts */
+		regmap_update_bits(regmap, REG_SPDIF_SIE, 0xffffff, 0);
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		scr = SCR_TXFIFO_AUTOSYNC | SCR_TXFIFO_CTRL_NORMAL |
+			SCR_TXSEL_NORMAL | SCR_USRC_SEL_CHIP |
+			SCR_TXFIFO_FSEL_IF8;
+		mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
+			SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
+			SCR_TXFIFO_FSEL_MASK;
+		for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+			clk_enable(spdif_priv->txclk[i]);
+	} else {
+		scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC;
+		mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
+			SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
+		clk_enable(spdif_priv->rxclk);
+	}
+	regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+	/* Power up SPDIF module */
+	regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
+
+	return 0;
+}
+
+static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *cpu_dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 scr, mask, i;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		scr = 0;
+		mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
+			SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
+			SCR_TXFIFO_FSEL_MASK;
+		for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+			clk_disable(spdif_priv->txclk[i]);
+	} else {
+		scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO;
+		mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
+			SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
+		clk_disable(spdif_priv->rxclk);
+	}
+	regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+	/* Power down SPDIF module only if tx&rx are both inactive */
+	if (!cpu_dai->active) {
+		spdif_intr_status_clear(spdif_priv);
+		regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, SCR_LOW_POWER);
+	}
+}
+
+static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 sample_rate = params_rate(params);
+	int ret = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret  = spdif_set_sample_rate(substream, sample_rate);
+		if (ret) {
+			dev_err(&pdev->dev, "%s: set sample rate failed: %d\n",
+					__func__, sample_rate);
+			return ret;
+		}
+		spdif_set_cstatus(ctrl, IEC958_AES3_CON_CLOCK,
+				IEC958_AES3_CON_CLOCK_1000PPM);
+		spdif_write_channel_status(spdif_priv);
+	} else {
+		/* setup rx clock source */
+		ret = spdif_set_rx_clksrc(spdif_priv, SPDIF_DEFAULT_GAINSEL, 1);
+	}
+
+	return ret;
+}
+
+static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
+				int cmd, struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	int is_playack = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	u32 intr = is_playack ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE;
+	u32 dmaen = is_playack ? SCR_DMA_TX_EN : SCR_DMA_RX_EN;;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		regmap_update_bits(regmap, REG_SPDIF_SIE, intr, intr);
+		regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, dmaen);
+		dumpregs(spdif_priv);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, 0);
+		regmap_update_bits(regmap, REG_SPDIF_SIE, intr, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct snd_soc_dai_ops fsl_spdif_dai_ops = {
+	.startup = fsl_spdif_startup,
+	.hw_params = fsl_spdif_hw_params,
+	.trigger = fsl_spdif_trigger,
+	.shutdown = fsl_spdif_shutdown,
+};
+
+
+/*
+ * ============================================
+ * FSL SPDIF IEC958 controller(mixer) functions
+ *
+ *	Channel status get/put control
+ *	User bit value get/put control
+ *	Valid bit value get control
+ *	DPLL lock status get control
+ *	User bit sync mode selection control
+ * ============================================
+ */
+
+static int fsl_spdif_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+
+	return 0;
+}
+
+static int fsl_spdif_pb_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *uvalue)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+
+	uvalue->value.iec958.status[0] = ctrl->ch_status[0];
+	uvalue->value.iec958.status[1] = ctrl->ch_status[1];
+	uvalue->value.iec958.status[2] = ctrl->ch_status[2];
+	uvalue->value.iec958.status[3] = ctrl->ch_status[3];
+
+	return 0;
+}
+
+static int fsl_spdif_pb_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *uvalue)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+
+	ctrl->ch_status[0] = uvalue->value.iec958.status[0];
+	ctrl->ch_status[1] = uvalue->value.iec958.status[1];
+	ctrl->ch_status[2] = uvalue->value.iec958.status[2];
+	ctrl->ch_status[3] = uvalue->value.iec958.status[3];
+
+	spdif_write_channel_status(spdif_priv);
+
+	return 0;
+}
+
+/* Get channel status from SPDIF_RX_CCHAN register */
+static int fsl_spdif_capture_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 cstatus, val;
+
+	regmap_read(regmap, REG_SPDIF_SIS, &val);
+	if (!(val & INT_CNEW)) {
+		return -EAGAIN;
+	}
+
+	regmap_read(regmap, REG_SPDIF_SRCSH, &cstatus);
+	ucontrol->value.iec958.status[0] = (cstatus >> 16) & 0xFF;
+	ucontrol->value.iec958.status[1] = (cstatus >> 8) & 0xFF;
+	ucontrol->value.iec958.status[2] = cstatus & 0xFF;
+
+	regmap_read(regmap, REG_SPDIF_SRCSL, &cstatus);
+	ucontrol->value.iec958.status[3] = (cstatus >> 16) & 0xFF;
+	ucontrol->value.iec958.status[4] = (cstatus >> 8) & 0xFF;
+	ucontrol->value.iec958.status[5] = cstatus & 0xFF;
+
+	/* clear intr */
+	regmap_write(regmap, REG_SPDIF_SIC, INT_CNEW);
+
+	return 0;
+}
+
+/*
+ * Get User bits (subcode) from chip value which readed out
+ * in UChannel register.
+ */
+static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&ctrl->ctl_lock, flags);
+	if (ctrl->ready_buf) {
+		int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE;
+		memcpy(&ucontrol->value.iec958.subcode[0],
+				&ctrl->subcode[idx], SPDIF_UBITS_SIZE);
+	} else {
+		ret = -EAGAIN;
+	}
+	spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
+
+	return ret;
+}
+
+/* Q-subcode infomation. The byte size is SPDIF_UBITS_SIZE/8 */
+static int fsl_spdif_qinfo(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = SPDIF_QSUB_SIZE;
+
+	return 0;
+}
+
+/* Get Q subcode from chip value which readed out in QChannel register */
+static int fsl_spdif_qget(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&ctrl->ctl_lock, flags);
+	if (ctrl->ready_buf) {
+		int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE;
+		memcpy(&ucontrol->value.bytes.data[0],
+				&ctrl->qsub[idx], SPDIF_QSUB_SIZE);
+	} else {
+		ret = -EAGAIN;
+	}
+	spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
+
+	return ret;
+}
+
+/* Valid bit infomation */
+static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+
+	return 0;
+}
+
+/* Get valid good bit from interrupt status register */
+static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 val;
+
+	val = regmap_read(regmap, REG_SPDIF_SIS, &val);
+	ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0;
+	regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD);
+
+	return 0;
+}
+
+/* DPLL lock infomation */
+static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 16000;
+	uinfo->value.integer.max = 96000;
+
+	return 0;
+}
+
+static u32 gainsel_multi[GAINSEL_MULTI_MAX] = {
+	24, 16, 12, 8, 6, 4, 3,
+};
+
+/* Get RX data clock rate given the SPDIF bus_clk */
+static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
+				enum spdif_gainsel gainsel)
+{
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u64 tmpval64, busclk_freq = 0;
+	u32 freqmeas, phaseconf;
+	enum spdif_rxclk_src clksrc;
+
+	regmap_read(regmap, REG_SPDIF_SRFM, &freqmeas);
+	regmap_read(regmap, REG_SPDIF_SRPC, &phaseconf);
+
+	clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
+	if (rx_clklist[clksrc].lock && (phaseconf & SRPC_DPLL_LOCKED)) {
+		/* get bus clock from system */
+		busclk_freq = clk_get_rate(spdif_priv->rxclk);
+	}
+
+	/* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
+	tmpval64 = (u64) busclk_freq * freqmeas;
+	do_div(tmpval64, gainsel_multi[gainsel] * 1024);
+	do_div(tmpval64, 128 * 1024);
+
+	dev_dbg(&pdev->dev, "FreqMeas: %d\n", (int)freqmeas);
+	dev_dbg(&pdev->dev, "BusclkFreq: %d\n", (int)busclk_freq);
+	dev_dbg(&pdev->dev, "RxRate: %d\n", (int)tmpval64);
+
+	return (int)tmpval64;
+}
+
+/*
+ * Get DPLL lock or not info from stable interrupt status register.
+ * User application must use this control to get locked,
+ * then can do next PCM operation
+ */
+static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	int rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL);
+
+	if (atomic_read(&spdif_priv->dpll_locked))
+		ucontrol->value.integer.value[0] = rate;
+	else
+		ucontrol->value.integer.value[0] = 0;
+
+	return 0;
+}
+
+/* User bit sync mode info */
+static int fsl_spdif_usync_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+
+	return 0;
+}
+
+/*
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int fsl_spdif_usync_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 val;
+
+	regmap_read(regmap, REG_SPDIF_SRCD, &val);
+	ucontrol->value.integer.value[0] = (val & SRCD_CD_USER) != 0;
+
+	return 0;
+}
+
+/*
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int fsl_spdif_usync_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 val = ucontrol->value.integer.value[0] << SRCD_CD_USER_OFFSET;
+
+	regmap_update_bits(regmap, REG_SPDIF_SRCD, SRCD_CD_USER, val);
+
+	return 0;
+}
+
+/* FSL SPDIF IEC958 controller defines */
+static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
+	/* status cchanel controller */
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_WRITE |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_info,
+		.get = fsl_spdif_pb_get,
+		.put = fsl_spdif_pb_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_info,
+		.get = fsl_spdif_capture_get,
+	},
+	/* user bits controller */
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = "IEC958 Subcode Capture Default",
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_info,
+		.get = fsl_spdif_subcode_get,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = "IEC958 Q-subcode Capture Default",
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_qinfo,
+		.get = fsl_spdif_qget,
+	},
+	/* valid bit error controller */
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = "IEC958 V-Bit Errors",
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_vbit_info,
+		.get = fsl_spdif_vbit_get,
+	},
+	/* DPLL lock info get controller */
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = "RX Sample Rate",
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_rxrate_info,
+		.get = fsl_spdif_rxrate_get,
+	},
+	/* User bit sync mode set/get controller */
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = "IEC958 USyncMode CDText",
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_WRITE |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_usync_info,
+		.get = fsl_spdif_usync_get,
+		.put = fsl_spdif_usync_put,
+	},
+};
+
+static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+	struct fsl_spdif_priv *spdif_private = snd_soc_dai_get_drvdata(dai);
+
+	dai->playback_dma_data = &spdif_private->dma_params_tx;
+	dai->capture_dma_data = &spdif_private->dma_params_rx;
+
+	snd_soc_add_dai_controls(dai, fsl_spdif_ctrls, ARRAY_SIZE(fsl_spdif_ctrls));
+
+	return 0;
+}
+
+struct snd_soc_dai_driver fsl_spdif_dai = {
+	.probe = &fsl_spdif_dai_probe,
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = FSL_SPDIF_RATES_PLAYBACK,
+		.formats = FSL_SPDIF_FORMATS_PLAYBACK,
+	},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = FSL_SPDIF_RATES_CAPTURE,
+		.formats = FSL_SPDIF_FORMATS_CAPTURE,
+	},
+	.ops = &fsl_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_spdif_component = {
+	.name		= "fsl-spdif",
+};
+
+/*
+ * ================
+ * FSL SPDIF REGMAP
+ * ================
+ */
+
+static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case REG_SPDIF_SCR:
+	case REG_SPDIF_SRCD:
+	case REG_SPDIF_SRPC:
+	case REG_SPDIF_SIE:
+	case REG_SPDIF_SIS:
+	case REG_SPDIF_SRL:
+	case REG_SPDIF_SRR:
+	case REG_SPDIF_SRCSH:
+	case REG_SPDIF_SRCSL:
+	case REG_SPDIF_SRU:
+	case REG_SPDIF_SRQ:
+	case REG_SPDIF_STCSCH:
+	case REG_SPDIF_STCSCL:
+	case REG_SPDIF_SRFM:
+	case REG_SPDIF_STC:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case REG_SPDIF_SCR:
+	case REG_SPDIF_SRCD:
+	case REG_SPDIF_SRPC:
+	case REG_SPDIF_SIE:
+	case REG_SPDIF_SIC:
+	case REG_SPDIF_STL:
+	case REG_SPDIF_STR:
+	case REG_SPDIF_STCSCH:
+	case REG_SPDIF_STCSCL:
+	case REG_SPDIF_STC:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg)
+{
+	/* Sync all registers after reset */
+	return true;
+}
+
+static const struct regmap_config fsl_spdif_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+
+	.max_register = REG_SPDIF_STC,
+	.readable_reg = fsl_spdif_readable_reg,
+	.writeable_reg = fsl_spdif_writeable_reg,
+	.volatile_reg = fsl_spdif_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static void spdif_clk_cal_txdiv(struct fsl_spdif_priv *spdif_priv)
+{
+	struct platform_device *pdev = spdif_priv->pdev;
+	struct clk **clk = spdif_priv->txclk;
+	u64 rate_ideal, rate_actual, sub, savesub;
+	u32 i, div, arate, rate[] = {32000, 44100, 48000};
+
+	for (i = 0; i < SPDIF_TXRATE_MAX; i++, savesub = 100000) {
+		for (div = 1; div <= 128; div++) {
+			rate_ideal = rate[i] * (div + 1) * 64;
+			rate_actual = clk_round_rate(clk[i], rate_ideal);
+
+			arate = rate_actual / 64;
+			arate /= div;
+			if (arate == rate[i]) {
+				savesub = 0;
+				spdif_priv->txclk_div[i] = div;
+				break;
+			} else if (arate / rate[i] == 1) {
+				sub = (arate - rate[i]) * 100000;
+				do_div(sub, rate[i]);
+				if (sub < savesub) {
+					savesub = sub;
+					spdif_priv->txclk_div[i] = div;
+				}
+			} else if (rate[i] / arate == 1) {
+				sub = (rate[i] - arate) * 100000;
+				do_div(sub, rate[i]);
+				if (sub < savesub) {
+					savesub = sub;
+					spdif_priv->txclk_div[i] = div;
+				}
+			}
+		}
+		dev_dbg(&pdev->dev, "calculated %dHz div: %d\n",
+				rate[i], spdif_priv->txclk_div[i]);
+	}
+}
+
+static int fsl_spdif_probe(struct platform_device *pdev)
+{
+	const char *p, *rate[] = {"tx-32000", "tx-44100", "tx-48000"};
+	struct fsl_spdif_priv *spdif_priv;
+	struct spdif_mixer_control *ctrl;
+	struct device_node *np = pdev->dev.of_node;
+	struct resource res;
+	void __iomem *regs;
+	int ret = 0, count, i, j;
+	bool lock = false;
+	char *name;
+
+	if (!of_device_is_available(np))
+		return -ENODEV;
+
+	/* The DAI name is the last part of the full name of the node. */
+	p = strrchr(np->full_name, '/') + 1;
+	spdif_priv = devm_kzalloc(&pdev->dev,
+			sizeof(struct fsl_spdif_priv) + strlen(p) + 1, GFP_KERNEL);
+	if (!spdif_priv) {
+		dev_err(&pdev->dev, "could not allocate DAI object\n");
+		return -ENOMEM;
+	}
+
+	strcpy(spdif_priv->name, p);
+
+	spdif_priv->pdev = pdev;
+
+	/* Initialize this copy of the CPU DAI driver structure */
+	memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
+	spdif_priv->cpu_dai_drv.name = spdif_priv->name;
+
+	/* Get the addresses and IRQ */
+	ret = of_address_to_resource(np, 0, &res);
+	if (ret) {
+		dev_err(&pdev->dev, "could not determine device resources\n");
+		return ret;
+	}
+
+	regs = of_iomap(np, 0);
+	if (IS_ERR(regs)) {
+		dev_err(&pdev->dev, "could not map device resources\n");
+		return PTR_ERR(regs);
+	}
+
+	spdif_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+			"core", regs, &fsl_spdif_regmap_config);
+	if (IS_ERR(spdif_priv->regmap)) {
+		dev_err(&pdev->dev, "regmap init failed\n");
+		ret = PTR_ERR(spdif_priv->regmap);
+		return ret;
+	}
+
+	spdif_priv->irq = irq_of_parse_and_map(np, 0);
+	if (spdif_priv->irq == NO_IRQ) {
+		dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
+		ret = -ENXIO;
+		goto error_iomap;
+	}
+
+	/* The 'name' should not have any slashes in it. */
+	ret = request_irq(spdif_priv->irq, spdif_isr, 0,
+			spdif_priv->name, spdif_priv);
+	if (ret) {
+		dev_err(&pdev->dev, "could not claim irq %u\n", spdif_priv->irq);
+		goto error_irqmap;
+	}
+
+	/* We use regmap to control the coreclk, so no need to prepare it */
+	spdif_priv->coreclk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(spdif_priv->coreclk)) {
+		ret = PTR_ERR(spdif_priv->coreclk);
+		dev_err(&pdev->dev, "failed to get clock: %d\n", ret);
+		goto error_irqreq;
+	}
+
+	spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rx");
+	if (IS_ERR(spdif_priv->rxclk)) {
+		/* Use coreclk as default rxclk */
+		spdif_priv->rxclk = spdif_priv->coreclk;
+		dev_warn(&pdev->dev, "using core clock as rx clk\n");
+	} else {
+		clk_prepare(spdif_priv->rxclk);
+	}
+
+	spdif_priv->txclk_main = devm_clk_get(&pdev->dev, "tx");
+	if (IS_ERR(spdif_priv->txclk_main)) {
+		/* Use coreclk as default txclk */
+		spdif_priv->txclk_main = spdif_priv->coreclk;
+		dev_warn(&pdev->dev, "using core clock as tx clk\n");
+	} else {
+		clk_prepare(spdif_priv->txclk_main);
+	}
+
+	for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+		spdif_priv->txclk[i] = devm_clk_get(&pdev->dev, rate[i]);
+		if (IS_ERR(spdif_priv->txclk[i])) {
+			/* Use txclk as default clksrc for 32000 sample rate */
+			spdif_priv->txclk[i] = spdif_priv->txclk_main;
+			dev_warn(&pdev->dev, "using tx clock as %s clk\n", rate[i]);
+		} else {
+			clk_prepare(spdif_priv->txclk[i]);
+		}
+	}
+
+	count = of_property_count_strings(np, "tx-clksrc-names");
+	if (!count) {
+		dev_err(&pdev->dev, "no tx clksrc name registered\n");
+		goto error_clk;
+	} else if (count > STC_CLK_MAX) {
+		dev_err(&pdev->dev, "too many tx clksrc names registered\n");
+		goto error_clk;
+	}
+
+	for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+		spdif_priv->txclk_src[i] = DEFAULT_TXCLK_SRC;
+
+	for (i = 0; i < count; i++) {
+		if (of_property_read_string_index(np, "tx-clksrc-names",
+					i, (const char **)&name))
+			continue;
+		if (!strncmp(name, "lock.", strlen("lock."))) {
+			tx_clklist[i].lock = true;
+			name = strstr(name, ".") + 1;
+		}
+
+		strncpy(tx_clklist[i].name, name, 16);
+
+		for (j = 0; j < SPDIF_TXRATE_MAX; j++) {
+			if (!strcmp(name, __clk_get_name(spdif_priv->txclk[j])))
+				spdif_priv->txclk_src[j] = i;
+		}
+	}
+
+	if (of_property_read_bool(np, "rx-clksrc-lock"))
+		lock = true;
+
+	count = of_property_count_strings(np, "rx-clksrc-names");
+	if (!count) {
+		dev_err(&pdev->dev, "no rx clksrc name registered\n");
+		goto error_clk;
+	} else if (count > SRPC_CLK_MAX) {
+		dev_err(&pdev->dev, "too many rx clksrc names registered\n");
+		goto error_clk;
+	}
+
+	spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC;
+	for (i = 0; i < count; i++) {
+		if (of_property_read_string_index(np, "rx-clksrc-names",
+					i, (const char **)&name))
+			continue;
+		if (!strncmp(name, "lock.", strlen("lock."))) {
+			rx_clklist[i].lock = true;
+			name = strstr(name, ".") + 1;
+		}
+
+		strncpy(rx_clklist[i].name, name, 16);
+
+		if (!strcmp(name, __clk_get_name(spdif_priv->rxclk))
+				&& !(rx_clklist[i].lock ^ lock))
+				spdif_priv->rxclk_src = i;
+	}
+
+	spdif_clk_cal_txdiv(spdif_priv);
+
+	ctrl = &spdif_priv->fsl_spdif_control;
+	/* initial spinlock for control data */
+	spin_lock_init(&ctrl->ctl_lock);
+
+	/* init tx channel status default value */
+	ctrl->ch_status[0] =
+		IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_5015;
+	ctrl->ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID;
+	ctrl->ch_status[2] = 0x00;
+	ctrl->ch_status[3] =
+		IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM;
+
+	atomic_set(&spdif_priv->dpll_locked, 0);
+
+	spdif_priv->dma_params_tx.maxburst = FSL_SPDIF_TXFIFO_WML;
+	spdif_priv->dma_params_rx.maxburst = FSL_SPDIF_RXFIFO_WML;
+	spdif_priv->dma_params_tx.addr = res.start + REG_SPDIF_STL;
+	spdif_priv->dma_params_rx.addr = res.start + REG_SPDIF_SRL;
+
+	/* Register with ASoC */
+	dev_set_drvdata(&pdev->dev, spdif_priv);
+
+	ret = snd_soc_register_component(&pdev->dev, &fsl_spdif_component,
+					 &spdif_priv->cpu_dai_drv, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
+		goto error_dev;
+	}
+
+	ret = imx_pcm_dma_init(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret);
+		goto error_component;
+	}
+
+	return ret;
+
+error_component:
+	snd_soc_unregister_component(&pdev->dev);
+error_dev:
+	dev_set_drvdata(&pdev->dev, NULL);
+error_clk:
+	for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+		if (spdif_priv->txclk[i] != spdif_priv->txclk_main)
+			clk_unprepare(spdif_priv->txclk[i]);
+	}
+	if (spdif_priv->txclk_main != spdif_priv->coreclk)
+		clk_unprepare(spdif_priv->txclk_main);
+	if (spdif_priv->rxclk != spdif_priv->coreclk)
+		clk_unprepare(spdif_priv->rxclk);
+error_irqreq:
+	free_irq(spdif_priv->irq, spdif_priv);
+error_irqmap:
+	irq_dispose_mapping(spdif_priv->irq);
+error_iomap:
+	iounmap(regs);
+
+	return ret;
+}
+
+static int fsl_spdif_remove(struct platform_device *pdev)
+{
+	struct fsl_spdif_priv *spdif_priv = platform_get_drvdata(pdev);
+	int i;
+
+	imx_pcm_dma_exit(pdev);
+	snd_soc_unregister_component(&pdev->dev);
+
+	for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+		if (spdif_priv->txclk[i] != spdif_priv->txclk_main)
+			clk_unprepare(spdif_priv->txclk[i]);
+	}
+	if (spdif_priv->txclk_main != spdif_priv->coreclk)
+		clk_unprepare(spdif_priv->txclk_main);
+	if (spdif_priv->rxclk != spdif_priv->coreclk)
+		clk_unprepare(spdif_priv->rxclk);
+
+	free_irq(spdif_priv->irq, spdif_priv);
+	irq_dispose_mapping(spdif_priv->irq);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id fsl_spdif_dt_ids[] = {
+	{ .compatible = "fsl,fsl-spdif", },
+	{ .compatible = "fsl,imx6q-spdif", },
+	{ .compatible = "fsl,imx6sl-spdif", },
+	{ .compatible = "fsl,imx53-spdif", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
+
+static struct platform_driver fsl_spdif_driver = {
+	.driver = {
+		.name = "fsl-spdif-dai",
+		.owner = THIS_MODULE,
+		.of_match_table = fsl_spdif_dt_ids,
+	},
+	.probe = fsl_spdif_probe,
+	.remove = fsl_spdif_remove,
+};
+
+module_platform_driver(fsl_spdif_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale S/PDIF CPU DAI Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:fsl_spdif");
diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h
new file mode 100644
index 0000000..feacc3a
--- /dev/null
+++ b/sound/soc/fsl/fsl_spdif.h
@@ -0,0 +1,223 @@
+/*
+ * fsl_spdif.h - ALSA S/PDIF interface for the Freescale i.MX SoC
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <b42378@freescale.com>
+ *
+ * Based on fsl_ssi.h
+ * Author: Timur Tabi <timur@freescale.com>
+ * Copyright 2007-2008 Freescale Semiconductor, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program  is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _FSL_SPDIF_DAI_H
+#define _FSL_SPDIF_DAI_H
+
+/* S/PDIF Register Map */
+#define REG_SPDIF_SCR 			0x0	/* SPDIF Configuration Register */
+#define REG_SPDIF_SRCD		 	0x4	/* CDText Control Register */
+#define REG_SPDIF_SRPC			0x8	/* PhaseConfig Register */
+#define REG_SPDIF_SIE			0xc	/* InterruptEn Register */
+#define REG_SPDIF_SIS			0x10	/* InterruptStat Register */
+#define REG_SPDIF_SIC			0x10	/* InterruptClear Register */
+#define REG_SPDIF_SRL			0x14	/* SPDIFRxLeft Register */
+#define REG_SPDIF_SRR			0x18	/* SPDIFRxRight Register */
+#define REG_SPDIF_SRCSH			0x1c	/* SPDIFRxCChannel_h Register */
+#define REG_SPDIF_SRCSL			0x20	/* SPDIFRxCChannel_l Register */
+#define REG_SPDIF_SRU			0x24	/* UchannelRx Register */
+#define REG_SPDIF_SRQ			0x28	/* QchannelRx Register */
+#define REG_SPDIF_STL			0x2C	/* SPDIFTxLeft Register */
+#define REG_SPDIF_STR			0x30	/* SPDIFTxRight Register */
+#define REG_SPDIF_STCSCH		0x34	/* SPDIFTxCChannelCons_h Register */
+#define REG_SPDIF_STCSCL		0x38	/* SPDIFTxCChannelCons_l Register */
+#define REG_SPDIF_SRFM			0x44	/* FreqMeas Register */
+#define REG_SPDIF_STC			0x50	/* SPDIFTxClk Register */
+
+
+/* SPDIF Configuration register */
+#define SCR_RXFIFO_CTL_OFFSET		23
+#define SCR_RXFIFO_CTL_MASK		(1 << SCR_RXFIFO_CTL_OFFSET)
+#define SCR_RXFIFO_CTL_ZERO		(1 << SCR_RXFIFO_CTL_OFFSET)
+#define SCR_RXFIFO_OFF_OFFSET		22
+#define SCR_RXFIFO_OFF_MASK		(1 << SCR_RXFIFO_OFF_OFFSET)
+#define SCR_RXFIFO_OFF			(1 << SCR_RXFIFO_OFF_OFFSET)
+#define SCR_RXFIFO_RST_OFFSET		21
+#define SCR_RXFIFO_RST_MASK		(1 << SCR_RXFIFO_RST_OFFSET)
+#define SCR_RXFIFO_RST			(1 << SCR_RXFIFO_RST_OFFSET)
+#define SCR_RXFIFO_FSEL_OFFSET		19
+#define SCR_RXFIFO_FSEL_MASK		(0x3 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF0		(0x0 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF4		(0x1 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF8		(0x2 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF12		(0x3 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_AUTOSYNC_OFFSET	18
+#define SCR_RXFIFO_AUTOSYNC_MASK	(1 << SCR_RXFIFO_AUTOSYNC_OFFSET)
+#define SCR_RXFIFO_AUTOSYNC		(1 << SCR_RXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_AUTOSYNC_OFFSET	17
+#define SCR_TXFIFO_AUTOSYNC_MASK	(1 << SCR_TXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_AUTOSYNC		(1 << SCR_TXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_FSEL_OFFSET		15
+#define SCR_TXFIFO_FSEL_MASK		(0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF0		(0x0 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF4		(0x1 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF8		(0x2 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF12		(0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_LOW_POWER			(1 << 13)
+#define SCR_SOFT_RESET			(1 << 12)
+#define SCR_TXFIFO_CTRL_OFFSET		10
+#define SCR_TXFIFO_CTRL_MASK		(0x3 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_ZERO		(0x0 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_NORMAL		(0x1 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_ONESAMPLE	(0x2 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_DMA_RX_EN_OFFSET		9
+#define SCR_DMA_RX_EN_MASK		(1 << SCR_DMA_RX_EN_OFFSET)
+#define SCR_DMA_RX_EN			(1 << SCR_DMA_RX_EN_OFFSET)
+#define SCR_DMA_TX_EN_OFFSET		8
+#define SCR_DMA_TX_EN_MASK		(1 << SCR_DMA_TX_EN_OFFSET)
+#define SCR_DMA_TX_EN			(1 << SCR_DMA_TX_EN_OFFSET)
+#define SCR_VAL_OFFSET			5
+#define SCR_VAL_MASK			(1 << SCR_VAL_OFFSET)
+#define SCR_VAL_CLEAR			(1 << SCR_VAL_OFFSET)
+#define SCR_TXSEL_OFFSET		2
+#define SCR_TXSEL_MASK			(0x7 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_OFF			(0 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_RX			(1 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_NORMAL		(0x5 << SCR_TXSEL_OFFSET)
+#define SCR_USRC_SEL_OFFSET		0x0
+#define SCR_USRC_SEL_MASK		(0x3 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_NONE		(0x0 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_RECV		(0x1 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_CHIP		(0x3 << SCR_USRC_SEL_OFFSET)
+
+/* SPDIF CDText control */
+#define SRCD_CD_USER_OFFSET		1
+#define SRCD_CD_USER			(1 << SRCD_CD_USER_OFFSET)
+
+/* SPDIF Phase Configuration register */
+#define SRPC_DPLL_LOCKED		(1 << 6)
+#define SRPC_CLKSRC_SEL_OFFSET		7
+#define SRPC_CLKSRC_SEL_MASK		(0xf << SRPC_CLKSRC_SEL_OFFSET)
+#define SRPC_CLKSRC_SEL_SET(x)		((x << SRPC_CLKSRC_SEL_OFFSET) & SRPC_CLKSRC_SEL_MASK)
+#define SRPC_CLKSRC_SEL_LOCKED		5
+#define SRPC_GAINSEL_OFFSET		3
+#define SRPC_GAINSEL_MASK		(0x7 << SRPC_GAINSEL_OFFSET)
+#define SRPC_GAINSEL_SET(x)		((x << SRPC_GAINSEL_OFFSET) & SRPC_GAINSEL_MASK)
+
+/* SPDIF rx clock source */
+enum spdif_rxclk_src {
+	SRPC_CLKSRC_0 = 0,
+	SRPC_CLKSRC_1,
+	SRPC_CLKSRC_2,
+	SRPC_CLKSRC_3,
+	SRPC_CLKSRC_4,
+	SRPC_CLKSRC_5,
+	SRPC_CLKSRC_6,
+	SRPC_CLKSRC_7,
+	SRPC_CLKSRC_8,
+	SRPC_CLKSRC_9,
+	SRPC_CLKSRC_10,
+	SRPC_CLKSRC_11,
+	SRPC_CLKSRC_12,
+	SRPC_CLKSRC_13,
+	SRPC_CLKSRC_14,
+	SRPC_CLKSRC_15,
+};
+#define SRPC_CLKSRC_MAX			(SRPC_CLKSRC_15 + 1)
+#define DEFAULT_RXCLK_SRC		SRPC_CLKSRC_0
+
+enum spdif_gainsel {
+	GAINSEL_MULTI_24 = 0,
+	GAINSEL_MULTI_16,
+	GAINSEL_MULTI_12,
+	GAINSEL_MULTI_8,
+	GAINSEL_MULTI_6,
+	GAINSEL_MULTI_4,
+	GAINSEL_MULTI_3,
+};
+#define GAINSEL_MULTI_MAX		(GAINSEL_MULTI_3 + 1)
+#define SPDIF_DEFAULT_GAINSEL		GAINSEL_MULTI_8
+
+/* SPDIF interrupt mask define */
+#define INT_DPLL_LOCKED			(1 << 20)
+#define INT_TXFIFO_UNOV			(1 << 19)
+#define INT_TXFIFO_RESYNC		(1 << 18)
+#define INT_CNEW			(1 << 17)
+#define INT_VAL_NOGOOD			(1 << 16)
+#define INT_SYM_ERR			(1 << 15)
+#define INT_BIT_ERR			(1 << 14)
+#define INT_URX_FUL			(1 << 10)
+#define INT_URX_OV			(1 << 9)
+#define INT_QRX_FUL			(1 << 8)
+#define INT_QRX_OV			(1 << 7)
+#define INT_UQ_SYNC			(1 << 6)
+#define INT_UQ_ERR			(1 << 5)
+#define INT_RXFIFO_UNOV			(1 << 4)
+#define INT_RXFIFO_RESYNC		(1 << 3)
+#define INT_LOSS_LOCK			(1 << 2)
+#define INT_TX_EM			(1 << 1)
+#define INT_RXFIFO_FUL			(1 << 0)
+
+/* SPDIF Clock register */
+#define STC_SYSCLK_DIV_OFFSET		11
+#define STC_SYSCLK_DIV_MASK		(0x1ff << STC_TXCLK_SRC_OFFSET)
+#define STC_SYSCLK_DIV(x)		((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK)
+#define STC_TXCLK_SRC_OFFSET		8
+#define STC_TXCLK_SRC_MASK		(0x7 << STC_TXCLK_SRC_OFFSET)
+#define STC_TXCLK_SRC_SET(x)		((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK)
+#define STC_TXCLK_ALL_EN_OFFSET		7
+#define STC_TXCLK_ALL_EN_MASK		(1 << STC_TXCLK_ALL_EN_OFFSET)
+#define STC_TXCLK_ALL_EN		(1 << STC_TXCLK_ALL_EN_OFFSET)
+#define STC_TXCLK_DIV_OFFSET		0
+#define STC_TXCLK_DIV_MASK		(0x7ff << STC_TXCLK_DIV_OFFSET)
+#define STC_TXCLK_DIV(x)		((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK)
+
+/* SPDIF tx clksrc */
+enum spdif_txclk_src {
+	STC_TXCLK_SRC_0 = 0,
+	STC_TXCLK_SRC_1,
+	STC_TXCLK_SRC_2,
+	STC_TXCLK_SRC_3,
+	STC_TXCLK_SRC_4,
+	STC_TXCLK_SRC_5,
+	STC_TXCLK_SRC_6,
+	STC_TXCLK_SRC_7,
+};
+#define STC_TXCLK_SRC_MAX		(STC_TXCLK_SRC_7 + 1)
+#define DEFAULT_TXCLK_SRC		STC_TXCLK_SRC_1
+
+/* SPDIF tx rate */
+enum spdif_txrate {
+	SPDIF_TXRATE_32000 = 0,
+	SPDIF_TXRATE_44100,
+	SPDIF_TXRATE_48000,
+};
+#define SPDIF_TXRATE_MAX		(SPDIF_TXRATE_48000 + 1)
+
+
+#define SPDIF_CSTATUS_BYTE		6
+#define SPDIF_UBITS_SIZE		96
+#define SPDIF_QSUB_SIZE			(SPDIF_UBITS_SIZE / 8)
+
+
+#define FSL_SPDIF_RATES_PLAYBACK	(SNDRV_PCM_RATE_32000 |	\
+					 SNDRV_PCM_RATE_44100 |	\
+					 SNDRV_PCM_RATE_48000)
+
+#define FSL_SPDIF_RATES_CAPTURE		(SNDRV_PCM_RATE_16000 | \
+					 SNDRV_PCM_RATE_32000 |	\
+					 SNDRV_PCM_RATE_44100 | \
+					 SNDRV_PCM_RATE_48000 |	\
+					 SNDRV_PCM_RATE_64000 | \
+					 SNDRV_PCM_RATE_96000)
+
+#define FSL_SPDIF_FORMATS_PLAYBACK	(SNDRV_PCM_FMTBIT_S16_LE | \
+					 SNDRV_PCM_FMTBIT_S20_3LE | \
+					 SNDRV_PCM_FMTBIT_S24_LE)
+
+#define FSL_SPDIF_FORMATS_CAPTURE	(SNDRV_PCM_FMTBIT_S24_LE)
+
+#endif /* _FSL_SPDIF_DAI_H */
-- 
1.7.1

^ permalink raw reply related

* [PATCH v4 resent 0/2] Add freescale S/PDIF CPU DAI and machine drivers
From: Nicolin Chen @ 2013-08-12 12:05 UTC (permalink / raw)
  To: broonie, lars, p.zabel
  Cc: devicetree, alsa-devel, linuxppc-dev, timur, rob.herring

Changelog:
v3->v4:
 * Use regmap for CPU DAI driver.
 * Use individual clock source for 32KHz, 44KHz, 48KHz playback.
 * Determine clock source configuration from 'clocks' entry.
 * Added imx53 to compatible list, merged imx6q and imx6dl in the list.
 * Improve the algorism of reverse_bits().
 * Dropped the unneeded clk_put().
v2->v3:
 * Removed a wrong tag from the commit of patch-1.
v1->v2:
 * Dropped one applied patch for spdif dummy codec drivers.
 * Use generic DMA DT binding.
 * Let spdif controller driver calculate the clock div.
 * Added one optional clock source for spdif tx.
 * Reivsed documentation accordingly.

Nicolin Chen (2):
  ASoC: fsl: Add S/PDIF CPU DAI driver
  ASoC: fsl: Add S/PDIF machine driver

 .../devicetree/bindings/sound/fsl,spdif.txt        |  100 ++
 .../devicetree/bindings/sound/imx-audio-spdif.txt  |   29 +
 sound/soc/fsl/Kconfig                              |   14 +
 sound/soc/fsl/Makefile                             |    4 +
 sound/soc/fsl/fsl_spdif.c                          | 1350 ++++++++++++++++++++
 sound/soc/fsl/fsl_spdif.h                          |  223 ++++
 sound/soc/fsl/imx-spdif.c                          |  134 ++
 7 files changed, 1854 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/fsl,spdif.txt
 create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
 create mode 100644 sound/soc/fsl/fsl_spdif.c
 create mode 100644 sound/soc/fsl/fsl_spdif.h
 create mode 100644 sound/soc/fsl/imx-spdif.c

^ permalink raw reply

* [PATCH v4 0/2] Add freescale S/PDIF CPU DAI and machine drivers
From: Nicolin Chen @ 2013-08-12 12:01 UTC (permalink / raw)
  To: broonie, lars, p.zabel
  Cc: devicetree, alsa-devel, linuxppc-dev, timur, rob.herring

Changelog:
v3->v4:
 * Use regmap for CPU DAI driver.
 * Use individual clock source for 32KHz, 44KHz, 48KHz playback.
 * Determine clock source configuration from 'clocks' entry.
 * Added imx53 to compatible list, merged imx6q and imx6dl in the list.
 * Improve the algorism of reverse_bits().
 * Dropped the unneeded clk_put().
v2->v3:
 * Removed a wrong tag from the commit of patch-1.
v1->v2:
 * Dropped one applied patch for spdif dummy codec drivers.
 * Use generic DMA DT binding.
 * Let spdif controller driver calculate the clock div.
 * Added one optional clock source for spdif tx.
 * Reivsed documentation accordingly.

Nicolin Chen (2):
  ASoC: fsl: Add S/PDIF CPU DAI driver
  ASoC: fsl: Add S/PDIF machine driver

 .../devicetree/bindings/sound/fsl,spdif.txt        |  100 ++
 .../devicetree/bindings/sound/imx-audio-spdif.txt  |   29 +
 sound/soc/fsl/Kconfig                              |   14 +
 sound/soc/fsl/Makefile                             |    4 +
 sound/soc/fsl/fsl_spdif.c                          | 1350 ++++++++++++++++++++
 sound/soc/fsl/fsl_spdif.h                          |  223 ++++
 sound/soc/fsl/imx-spdif.c                          |  134 ++
 7 files changed, 1854 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/fsl,spdif.txt
 create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
 create mode 100644 sound/soc/fsl/fsl_spdif.c
 create mode 100644 sound/soc/fsl/fsl_spdif.h
 create mode 100644 sound/soc/fsl/imx-spdif.c

^ permalink raw reply

* [PATCH v4 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Nicolin Chen @ 2013-08-12 12:01 UTC (permalink / raw)
  To: broonie, lars, p.zabel
  Cc: devicetree, alsa-devel, linuxppc-dev, timur, rob.herring
In-Reply-To: <1376308870-14232-1-git-send-email-b42378@freescale.com>

This patch add S/PDIF controller driver for Freescale SoC.

Signed-off-by: Nicolin Chen <b42378@freescale.com>
---
 .../devicetree/bindings/sound/fsl,spdif.txt        |  100 ++
 sound/soc/fsl/Kconfig                              |    3 +
 sound/soc/fsl/Makefile                             |    2 +
 sound/soc/fsl/fsl_spdif.c                          | 1350 ++++++++++++++++++++
 sound/soc/fsl/fsl_spdif.h                          |  223 ++++
 5 files changed, 1678 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/fsl,spdif.txt
 create mode 100644 sound/soc/fsl/fsl_spdif.c
 create mode 100644 sound/soc/fsl/fsl_spdif.h

diff --git a/Documentation/devicetree/bindings/sound/fsl,spdif.txt b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
new file mode 100644
index 0000000..e95318e
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
@@ -0,0 +1,100 @@
+Freescale Sony/Philips Digital Interface Format (S/PDIF) Controller
+
+The Freescale S/PDIF audio block is a stereo transceiver that allows the
+processor to receive and transmit digital audio via an coaxial cable or
+a fibre cable.
+
+Required properties:
+
+  - compatible : Compatible list, contains "fsl,<chip>-spdif". Using general
+  "fsl,fsl-spdif" will get the default SoC type -- imx6q-spdif.
+
+  - reg : Offset and length of the register set for the device.
+
+  - interrupts : Contains spdif interrupt.
+
+  - dmas : Generic dma devicetree binding as described in
+  Documentation/devicetree/bindings/dma/dma.txt.
+
+  - dma-names : Two dmas have to be defined, "tx" and "rx".
+
+  - clocks : Contains an entry for each entry in clock-names.
+
+  - clock-names : Includes the following entries:
+	name		type		comments
+	"core"		Required	The core clock of spdif controller
+	"rx"		Optional	Rx clock source for spdif record.
+					If absent, will use core clock.
+	"tx"		Optional	Tx clock source for spdif playback.
+					If absent, will use core clock.
+	"tx-32000"	Optional	Tx clock source for 32000Hz sample rate
+					playback. If absent, will use tx clock.
+	"tx-44100"	Optional	Tx clock source for 44100Hz sample rate
+					playback. If absent, will use tx clock.
+	"tx-48000"	Optional	Tx clock source for 48000Hz sample rate
+					playback. If absent, will use tx clock.
+
+  - tx-clksrc-names : The names for all available clock sources for tx, which
+  is also being listed in SoC reference manual, ClkSrc_Sel bit of SPDIF_SRPC.
+  And the name list would be different between different SoC. Use 'null' for
+  those unlisted names, and the max number of tx-clksrc-names should be 8.
+
+  - rx-clksrc-names : The names for all available clock sources for rx, which
+  is also being listed in SoC reference manual, TxClk_Source bit of SPDIF_STC.
+  And the name list would be different between different SoC. Use 'null' for
+  those unlisted names, and the max number of rx-clksrc-names should be 16.
+
+Optional properties:
+
+  - rx-clksrc-lock: This is a boolean property. If present, ClkSrc_Sel bit
+  of SPDIF_SRPC would be set a clock source that cares DPLL locked condition.
+
+Example1:
+
+spdif: spdif@02004000 {
+	compatible = "fsl,imx6q-spdif";
+	reg = <0x02004000 0x4000>;
+	interrupts = <0 52 0x04>;
+	dmas = <&sdma 14 18 0>,
+	       <&sdma 15 18 0>;
+	dma-names = "rx", "tx";
+
+	clocks = <&clks 197>;
+	clock-names = "core";
+	rx-clksrc-lock;
+	rx-clksrc-names =
+		"lock.ext", "lock.spdif", "lock.asrc",
+		"lock.spdif_ext", "lock.esai", "ext",
+		"spdif", "asrc", "spdif_ext", "esai",
+		"lock.mlb", "lock.mlb_phy", "mlb",
+		"mlb_phy";
+	tx-clksrc-names =
+		"xtal", "spdif", "asrc", "spdif_ext",
+		"esai", "ipg", "mlb", "mlb_phy";
+
+	status = "okay";
+};
+
+Example2:
+
+spdif: spdif@02004000 {
+	compatible = "fsl,imx6sl-spdif";
+	reg = <0x02004000 0x4000>;
+	interrupts = <0 52 0x04>;
+	dmas = <&sdma 14 18 0>,
+	       <&sdma 15 18 0>;
+	dma-names = "rx", "tx";
+
+	clocks = <&clks 122>;
+	clock-names = "core";
+	rx-clksrc-lock;
+	rx-clksrc-names =
+		"lock.xtal", "lock.spdif", "null", "lock.spdif_ext",
+		"null", "xtal", "spdif", "null", "spdif_ext", "null",
+		"null", "null", "mlb";
+	tx-clksrc-names =
+		"xtal", "spdif", "null", "spdif_ext",
+		"null", "ipg";
+
+	status = "okay";
+};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index e15f771..2c518db 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,6 +1,9 @@
 config SND_SOC_FSL_SSI
 	tristate
 
+config SND_SOC_FSL_SPDIF
+	tristate
+
 config SND_SOC_FSL_UTILS
 	tristate
 
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index d4b4aa8..4b5970e 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -12,9 +12,11 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
 
 # Freescale PowerPC SSI/DMA Platform Support
 snd-soc-fsl-ssi-objs := fsl_ssi.o
+snd-soc-fsl-spdif-objs := fsl_spdif.o
 snd-soc-fsl-utils-objs := fsl_utils.o
 snd-soc-fsl-dma-objs := fsl_dma.o
 obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
+obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
 obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
 obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
 
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
new file mode 100644
index 0000000..c99f50d
--- /dev/null
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -0,0 +1,1350 @@
+/*
+ * Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * Based on stmp3xxx_spdif_dai.c
+ * Vladimir Barinov <vbarinov@embeddedalley.com>
+ * Copyright 2008 SigmaTel, Inc
+ * Copyright 2008 Embedded Alley Solutions, Inc
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program  is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/clk-private.h>
+#include <linux/regmap.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include <sound/asoundef.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "fsl_spdif.h"
+#include "imx-pcm.h"
+
+#define FSL_SPDIF_TXFIFO_WML      0x8
+#define FSL_SPDIF_RXFIFO_WML      0x8
+
+#define INTR_FOR_PLAYBACK (INT_TXFIFO_RESYNC)
+#define INTR_FOR_CAPTURE (INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL | INT_URX_OV|\
+		INT_QRX_FUL | INT_QRX_OV | INT_UQ_SYNC | INT_UQ_ERR |\
+		INT_RXFIFO_RESYNC | INT_LOSS_LOCK | INT_DPLL_LOCKED)
+
+#define SRPC_CLK_MAX 16
+#define STC_CLK_MAX 8
+static struct {
+	char name[16];
+	bool lock;
+}tx_clklist[SRPC_CLK_MAX], rx_clklist[STC_CLK_MAX];
+
+/*
+ * SPDIF control structure
+ * Defines channel status, subcode and Q sub
+ */
+struct spdif_mixer_control {
+	/* spinlock to access control data */
+	spinlock_t ctl_lock;
+
+	/* IEC958 channel tx status bit */
+	unsigned char ch_status[4];
+
+	/* User bits */
+	unsigned char subcode[2 * SPDIF_UBITS_SIZE];
+
+	/* Q subcode part of user bits */
+	unsigned char qsub[2 * SPDIF_QSUB_SIZE];
+
+	/* buffer ptrs for writer */
+	u32 upos;
+	u32 qpos;
+
+	/* ready buffer index of the two buffers */
+	u32 ready_buf;
+};
+
+struct fsl_spdif_priv {
+	struct spdif_mixer_control fsl_spdif_control;
+	struct snd_soc_dai_driver cpu_dai_drv;
+	struct platform_device *pdev;
+	struct regmap *regmap;
+	atomic_t dpll_locked;
+	u32 irq;
+	u8 rxclk_src;
+	u8 txclk_src[3];
+	u8 txclk_div[SPDIF_TXRATE_MAX];
+	struct clk *txclk[3];
+	struct clk *txclk_main;
+	struct clk *rxclk;
+	struct clk *coreclk;
+	struct snd_dmaengine_dai_dma_data dma_params_tx;
+	struct snd_dmaengine_dai_dma_data dma_params_rx;
+
+	/* The name space will be allocated dynamically */
+	char name[0];
+};
+
+
+#ifdef DEBUG
+static void dumpregs(struct fsl_spdif_priv *spdif_priv)
+{
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 val, i;
+	int ret;
+
+	/* Valid address set of SPDIF is {[0x0-0x38], 0x44, 0x50} */
+	for (i = 0 ; i <= REG_SPDIF_STC; i += 4) {
+		ret = regmap_read(regmap, REG_SPDIF_SCR + i, &val);
+		if (!ret)
+			dev_dbg(&pdev->dev, "REG 0x%02x = 0x%06x\n", i, val);
+	}
+}
+#else
+static void dumpregs(struct fsl_spdif_priv *spdif_priv) {}
+#endif
+
+
+/* DPLL locked and lock loss interrupt handler */
+static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv)
+{
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 locked;
+
+	regmap_read(regmap, REG_SPDIF_SRPC, &locked);
+	locked &= SRPC_DPLL_LOCKED;
+
+	dev_dbg(&pdev->dev, "isr: Rx dpll %s \n",
+			locked ? "locked" : "loss lock");
+
+	atomic_set(&spdif_priv->dpll_locked, locked ? 1 : 0);
+}
+
+/* Receiver found illegal symbol interrupt handler */
+static void spdif_irq_sym_error(struct fsl_spdif_priv *spdif_priv)
+{
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+
+	dev_dbg(&pdev->dev, "isr: receiver found illegal symbol\n");
+
+	if (!atomic_read(&spdif_priv->dpll_locked)) {
+		/* dpll unlocked seems no audio stream */
+		regmap_update_bits(regmap, REG_SPDIF_SIE, INT_SYM_ERR, 0);
+	}
+}
+
+/* U/Q Channel receive register full */
+static void spdif_irq_uqrx_full(struct fsl_spdif_priv *spdif_priv, char name)
+{
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 *pos, size, val, reg;
+
+	switch (name) {
+	case 'U':
+		pos = &ctrl->upos;
+		size = SPDIF_UBITS_SIZE;
+		reg = REG_SPDIF_SRU;
+		break;
+	case 'Q':
+		pos = &ctrl->qpos;
+		size = SPDIF_QSUB_SIZE;
+		reg = REG_SPDIF_SRQ;
+		break;
+	default:
+		return;
+	}
+
+	dev_dbg(&pdev->dev, "isr: %c Channel receive register full\n", name);
+
+	if (*pos >= size * 2) {
+		*pos = 0;
+	} else if (unlikely((*pos % size) + 3 > size)) {
+		dev_err(&pdev->dev, "User bit receivce buffer overflow\n");
+		return;
+	}
+
+	regmap_read(regmap, reg, &val);
+	ctrl->subcode[*pos++] = val >> 16;
+	ctrl->subcode[*pos++] = val >> 8;
+	ctrl->subcode[*pos++] = val;
+}
+
+/* U/Q Channel sync found */
+static void spdif_irq_uq_sync(struct fsl_spdif_priv *spdif_priv)
+{
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	struct platform_device *pdev = spdif_priv->pdev;
+
+	dev_dbg(&pdev->dev, "isr: U/Q Channel sync found\n");
+
+	/* U/Q buffer reset */
+	if (ctrl->qpos == 0)
+		return;
+
+	/* set ready to this buffer */
+	ctrl->ready_buf = (ctrl->qpos - 1) / SPDIF_QSUB_SIZE + 1;
+}
+
+/* U/Q Channel framing error */
+static void spdif_irq_uq_err(struct fsl_spdif_priv *spdif_priv)
+{
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 val;
+
+	dev_dbg(&pdev->dev, "isr: U/Q Channel framing error\n");
+
+	/* read U/Q data and do buffer reset */
+	regmap_read(regmap, REG_SPDIF_SRU, &val);
+	regmap_read(regmap, REG_SPDIF_SRQ, &val);
+
+	/* drop this U/Q buffer */
+	ctrl->ready_buf = 0;
+	ctrl->upos = 0;
+	ctrl->qpos = 0;
+}
+
+/* Get spdif interrupt status and clear the interrupt */
+static u32 spdif_intr_status_clear(struct fsl_spdif_priv *spdif_priv)
+{
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 val, val2;
+
+	regmap_read(regmap, REG_SPDIF_SIS, &val);
+	regmap_read(regmap, REG_SPDIF_SIE, &val2);
+
+	regmap_write(regmap, REG_SPDIF_SIC, val & val2);
+
+	return val;
+}
+
+static irqreturn_t spdif_isr(int irq, void *devid)
+{
+	struct fsl_spdif_priv *spdif_priv = (struct fsl_spdif_priv *)devid;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 sis;
+
+	sis = spdif_intr_status_clear(spdif_priv);
+
+	if (sis & INT_DPLL_LOCKED)
+		spdif_irq_dpll_lock(spdif_priv);
+
+	if (sis & INT_TXFIFO_UNOV)
+		dev_dbg(&pdev->dev, "isr: Tx FIFO under/overrun\n");
+
+	if (sis & INT_TXFIFO_RESYNC)
+		dev_dbg(&pdev->dev, "isr: Tx FIFO resync\n");
+
+	if (sis & INT_CNEW)
+		dev_dbg(&pdev->dev, "isr: cstatus new\n");
+
+	if (sis & INT_VAL_NOGOOD)
+		dev_dbg(&pdev->dev, "isr: validity flag no good\n");
+
+	if (sis & INT_SYM_ERR)
+		spdif_irq_sym_error(spdif_priv);
+
+	if (sis & INT_BIT_ERR)
+		dev_dbg(&pdev->dev, "isr: receiver found parity bit error\n");
+
+	if (sis & INT_URX_FUL)
+		spdif_irq_uqrx_full(spdif_priv, 'U');
+
+	if (sis & INT_URX_OV)
+		dev_dbg(&pdev->dev, "isr: U Channel receive register overrun\n");
+
+	if (sis & INT_QRX_FUL)
+		spdif_irq_uqrx_full(spdif_priv, 'Q');
+
+	if (sis & INT_QRX_OV)
+		dev_dbg(&pdev->dev, "isr: Q Channel receive register overrun\n");
+
+	if (sis & INT_UQ_SYNC)
+		spdif_irq_uq_sync(spdif_priv);
+
+	if (sis & INT_UQ_ERR)
+		spdif_irq_uq_err(spdif_priv);
+
+	if (sis & INT_RXFIFO_UNOV)
+		dev_dbg(&pdev->dev, "isr: Rx FIFO under/overrun\n");
+
+	if (sis & INT_RXFIFO_RESYNC)
+		dev_dbg(&pdev->dev, "isr: Rx FIFO resync\n");
+
+	if (sis & INT_LOSS_LOCK)
+		spdif_irq_dpll_lock(spdif_priv);
+
+	/* FIXME: Write Tx FIFO to clear TxEm */
+	if (sis & INT_TX_EM)
+		dev_dbg(&pdev->dev, "isr: Tx FIFO empty\n");
+
+	/* FIXME: Read Rx FIFO to clear RxFIFOFul */
+	if (sis & INT_RXFIFO_FUL)
+		dev_dbg(&pdev->dev, "isr: Rx FIFO full\n");
+
+	return IRQ_HANDLED;
+}
+
+static void spdif_softreset(struct fsl_spdif_priv *spdif_priv)
+{
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 val, cycle = 1000;
+
+	regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET);
+	regcache_sync(regmap);
+
+	/* RESET bit would be cleared after finishing its reset procedure */
+	do {
+		regmap_read(regmap, REG_SPDIF_SCR, &val);
+	} while ((val & SCR_SOFT_RESET) && cycle--);
+}
+
+static void spdif_set_cstatus(struct spdif_mixer_control *ctrl,
+				u8 mask, u8 cstatus)
+{
+	ctrl->ch_status[3] &= ~mask;
+	ctrl->ch_status[3] |= cstatus & mask;
+}
+
+static u8 reverse_bits(u8 input)
+{
+	u8 tmp = input;
+
+	tmp = ((tmp & 0b10101010) >> 1) | ((tmp << 1) & 0b10101010);
+	tmp = ((tmp & 0b11001100) >> 2) | ((tmp << 2) & 0b11001100);
+	tmp = ((tmp & 0b11110000) >> 4) | ((tmp << 4) & 0b11110000);
+
+	return tmp;
+}
+
+static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv)
+{
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 ch_status;
+
+	ch_status = (reverse_bits(ctrl->ch_status[0]) << 16) |
+		(reverse_bits(ctrl->ch_status[1]) << 8) |
+		reverse_bits(ctrl->ch_status[2]);
+	regmap_write(regmap, REG_SPDIF_STCSCH, ch_status);
+
+	dev_dbg(&pdev->dev, "STCSCH: 0x%06x\n", ch_status);
+
+	ch_status = reverse_bits(ctrl->ch_status[3]) << 16;
+	regmap_write(regmap, REG_SPDIF_STCSCL, ch_status);
+
+	dev_dbg(&pdev->dev, "STCSCL: 0x%06x\n", ch_status);
+}
+
+/* Set SPDIF PhaseConfig register for rx clock */
+static int spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv,
+				enum spdif_gainsel gainsel, int dpll_locked)
+{
+	enum spdif_rxclk_src clksrc = spdif_priv->rxclk_src;
+	struct regmap *regmap = spdif_priv->regmap;
+
+	if (clksrc >= SRPC_CLKSRC_MAX || gainsel >= GAINSEL_MULTI_MAX)
+		return -EINVAL;
+
+	if (!dpll_locked && rx_clklist[clksrc].lock)
+		clksrc += SRPC_CLKSRC_SEL_LOCKED;
+
+	regmap_update_bits(regmap, REG_SPDIF_SRPC,
+			SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
+			SRPC_CLKSRC_SEL_SET(clksrc) | SRPC_GAINSEL_SET(gainsel));
+
+	return 0;
+}
+
+static int spdif_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	unsigned long rate_actual;
+
+	rate_actual = clk_round_rate(clk, rate);
+	clk_set_rate(clk, rate_actual);
+
+	return 0;
+}
+
+static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
+				int sample_rate)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	unsigned long clk = -1, div = 1, csfs = 0;
+	u32 stc, mask, rate;
+
+	switch (sample_rate) {
+	case 32000:
+		rate = SPDIF_TXRATE_32000;
+		csfs = IEC958_AES3_CON_FS_32000;
+		break;
+	case 44100:
+		rate = SPDIF_TXRATE_44100;
+		csfs = IEC958_AES3_CON_FS_44100;
+		break;
+	case 48000:
+		rate = SPDIF_TXRATE_48000;
+		csfs = IEC958_AES3_CON_FS_48000;
+		break;
+	default:
+		dev_err(&pdev->dev, "unsupported samplerate %d\n", sample_rate);
+		return -EINVAL;
+	}
+
+	clk = spdif_priv->txclk_src[rate];
+	if (clk < 0) {
+		dev_err(&pdev->dev, "no defined %d clk src\n", sample_rate);
+		return -EINVAL;
+	}
+
+	div = spdif_priv->txclk_div[rate];
+	if (div == 0) {
+		dev_err(&pdev->dev, "tx clock source is dividing by zero\n");
+		return -EINVAL;
+	}
+	/*
+	 * The S/PDIF block needs a clock of 64 * fs * div.  The S/PDIF block
+	 * will divide by (div).  So request 64 * fs * (div+1) which will
+	 * get rounded.
+	 */
+	spdif_clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1));
+
+	dev_dbg(&pdev->dev, "expected clock rate = %d\n",
+			(int)(64 * sample_rate * div));
+	dev_dbg(&pdev->dev, "acutal clock rate = %d\n",
+			(int)clk_get_rate(spdif_priv->txclk[rate]));
+
+	/* set fs field in consumer channel status */
+	spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
+
+	/* select clock source and divisor */
+	stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div);
+	mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK;
+	regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
+
+	dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate);
+
+	return 0;
+}
+
+int fsl_spdif_startup(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *cpu_dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 scr, mask, i;
+
+	/* Reset module and interrupts only for first initialization */
+	if (!cpu_dai->active) {
+		spdif_softreset(spdif_priv);
+
+		/* disable all the interrupts */
+		regmap_update_bits(regmap, REG_SPDIF_SIE, 0xffffff, 0);
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		scr = SCR_TXFIFO_AUTOSYNC | SCR_TXFIFO_CTRL_NORMAL |
+			SCR_TXSEL_NORMAL | SCR_USRC_SEL_CHIP |
+			SCR_TXFIFO_FSEL_IF8;
+		mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
+			SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
+			SCR_TXFIFO_FSEL_MASK;
+		for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+			clk_enable(spdif_priv->txclk[i]);
+	} else {
+		scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC;
+		mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
+			SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
+		clk_enable(spdif_priv->rxclk);
+	}
+	regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+	/* Power up SPDIF module */
+	regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
+
+	return 0;
+}
+
+static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *cpu_dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 scr, mask, i;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		scr = 0;
+		mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
+			SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
+			SCR_TXFIFO_FSEL_MASK;
+		for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+			clk_disable(spdif_priv->txclk[i]);
+	} else {
+		scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO;
+		mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
+			SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
+		clk_disable(spdif_priv->rxclk);
+	}
+	regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+	/* Power down SPDIF module only if tx&rx are both inactive */
+	if (!cpu_dai->active) {
+		spdif_intr_status_clear(spdif_priv);
+		regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, SCR_LOW_POWER);
+	}
+}
+
+static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u32 sample_rate = params_rate(params);
+	int ret = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret  = spdif_set_sample_rate(substream, sample_rate);
+		if (ret) {
+			dev_err(&pdev->dev, "%s: set sample rate failed: %d\n",
+					__func__, sample_rate);
+			return ret;
+		}
+		spdif_set_cstatus(ctrl, IEC958_AES3_CON_CLOCK,
+				IEC958_AES3_CON_CLOCK_1000PPM);
+		spdif_write_channel_status(spdif_priv);
+	} else {
+		/* setup rx clock source */
+		ret = spdif_set_rx_clksrc(spdif_priv, SPDIF_DEFAULT_GAINSEL, 1);
+	}
+
+	return ret;
+}
+
+static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
+				int cmd, struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	int is_playack = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	u32 intr = is_playack ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE;
+	u32 dmaen = is_playack ? SCR_DMA_TX_EN : SCR_DMA_RX_EN;;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		regmap_update_bits(regmap, REG_SPDIF_SIE, intr, intr);
+		regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, dmaen);
+		dumpregs(spdif_priv);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, 0);
+		regmap_update_bits(regmap, REG_SPDIF_SIE, intr, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct snd_soc_dai_ops fsl_spdif_dai_ops = {
+	.startup = fsl_spdif_startup,
+	.hw_params = fsl_spdif_hw_params,
+	.trigger = fsl_spdif_trigger,
+	.shutdown = fsl_spdif_shutdown,
+};
+
+
+/*
+ * ============================================
+ * FSL SPDIF IEC958 controller(mixer) functions
+ *
+ *	Channel status get/put control
+ *	User bit value get/put control
+ *	Valid bit value get control
+ *	DPLL lock status get control
+ *	User bit sync mode selection control
+ * ============================================
+ */
+
+static int fsl_spdif_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+
+	return 0;
+}
+
+static int fsl_spdif_pb_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *uvalue)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+
+	uvalue->value.iec958.status[0] = ctrl->ch_status[0];
+	uvalue->value.iec958.status[1] = ctrl->ch_status[1];
+	uvalue->value.iec958.status[2] = ctrl->ch_status[2];
+	uvalue->value.iec958.status[3] = ctrl->ch_status[3];
+
+	return 0;
+}
+
+static int fsl_spdif_pb_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *uvalue)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+
+	ctrl->ch_status[0] = uvalue->value.iec958.status[0];
+	ctrl->ch_status[1] = uvalue->value.iec958.status[1];
+	ctrl->ch_status[2] = uvalue->value.iec958.status[2];
+	ctrl->ch_status[3] = uvalue->value.iec958.status[3];
+
+	spdif_write_channel_status(spdif_priv);
+
+	return 0;
+}
+
+/* Get channel status from SPDIF_RX_CCHAN register */
+static int fsl_spdif_capture_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 cstatus, val;
+
+	regmap_read(regmap, REG_SPDIF_SIS, &val);
+	if (!(val & INT_CNEW)) {
+		return -EAGAIN;
+	}
+
+	regmap_read(regmap, REG_SPDIF_SRCSH, &cstatus);
+	ucontrol->value.iec958.status[0] = (cstatus >> 16) & 0xFF;
+	ucontrol->value.iec958.status[1] = (cstatus >> 8) & 0xFF;
+	ucontrol->value.iec958.status[2] = cstatus & 0xFF;
+
+	regmap_read(regmap, REG_SPDIF_SRCSL, &cstatus);
+	ucontrol->value.iec958.status[3] = (cstatus >> 16) & 0xFF;
+	ucontrol->value.iec958.status[4] = (cstatus >> 8) & 0xFF;
+	ucontrol->value.iec958.status[5] = cstatus & 0xFF;
+
+	/* clear intr */
+	regmap_write(regmap, REG_SPDIF_SIC, INT_CNEW);
+
+	return 0;
+}
+
+/*
+ * Get User bits (subcode) from chip value which readed out
+ * in UChannel register.
+ */
+static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&ctrl->ctl_lock, flags);
+	if (ctrl->ready_buf) {
+		int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE;
+		memcpy(&ucontrol->value.iec958.subcode[0],
+				&ctrl->subcode[idx], SPDIF_UBITS_SIZE);
+	} else {
+		ret = -EAGAIN;
+	}
+	spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
+
+	return ret;
+}
+
+/* Q-subcode infomation. The byte size is SPDIF_UBITS_SIZE/8 */
+static int fsl_spdif_qinfo(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = SPDIF_QSUB_SIZE;
+
+	return 0;
+}
+
+/* Get Q subcode from chip value which readed out in QChannel register */
+static int fsl_spdif_qget(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&ctrl->ctl_lock, flags);
+	if (ctrl->ready_buf) {
+		int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE;
+		memcpy(&ucontrol->value.bytes.data[0],
+				&ctrl->qsub[idx], SPDIF_QSUB_SIZE);
+	} else {
+		ret = -EAGAIN;
+	}
+	spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
+
+	return ret;
+}
+
+/* Valid bit infomation */
+static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+
+	return 0;
+}
+
+/* Get valid good bit from interrupt status register */
+static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 val;
+
+	val = regmap_read(regmap, REG_SPDIF_SIS, &val);
+	ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0;
+	regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD);
+
+	return 0;
+}
+
+/* DPLL lock infomation */
+static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 16000;
+	uinfo->value.integer.max = 96000;
+
+	return 0;
+}
+
+static u32 gainsel_multi[GAINSEL_MULTI_MAX] = {
+	24, 16, 12, 8, 6, 4, 3,
+};
+
+/* Get RX data clock rate given the SPDIF bus_clk */
+static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
+				enum spdif_gainsel gainsel)
+{
+	struct regmap *regmap = spdif_priv->regmap;
+	struct platform_device *pdev = spdif_priv->pdev;
+	u64 tmpval64, busclk_freq = 0;
+	u32 freqmeas, phaseconf;
+	enum spdif_rxclk_src clksrc;
+
+	regmap_read(regmap, REG_SPDIF_SRFM, &freqmeas);
+	regmap_read(regmap, REG_SPDIF_SRPC, &phaseconf);
+
+	clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
+	if (rx_clklist[clksrc].lock && (phaseconf & SRPC_DPLL_LOCKED)) {
+		/* get bus clock from system */
+		busclk_freq = clk_get_rate(spdif_priv->rxclk);
+	}
+
+	/* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
+	tmpval64 = (u64) busclk_freq * freqmeas;
+	do_div(tmpval64, gainsel_multi[gainsel] * 1024);
+	do_div(tmpval64, 128 * 1024);
+
+	dev_dbg(&pdev->dev, "FreqMeas: %d\n", (int)freqmeas);
+	dev_dbg(&pdev->dev, "BusclkFreq: %d\n", (int)busclk_freq);
+	dev_dbg(&pdev->dev, "RxRate: %d\n", (int)tmpval64);
+
+	return (int)tmpval64;
+}
+
+/*
+ * Get DPLL lock or not info from stable interrupt status register.
+ * User application must use this control to get locked,
+ * then can do next PCM operation
+ */
+static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	int rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL);
+
+	if (atomic_read(&spdif_priv->dpll_locked))
+		ucontrol->value.integer.value[0] = rate;
+	else
+		ucontrol->value.integer.value[0] = 0;
+
+	return 0;
+}
+
+/* User bit sync mode info */
+static int fsl_spdif_usync_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+
+	return 0;
+}
+
+/*
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int fsl_spdif_usync_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 val;
+
+	regmap_read(regmap, REG_SPDIF_SRCD, &val);
+	ucontrol->value.integer.value[0] = (val & SRCD_CD_USER) != 0;
+
+	return 0;
+}
+
+/*
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int fsl_spdif_usync_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+	struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct regmap *regmap = spdif_priv->regmap;
+	u32 val = ucontrol->value.integer.value[0] << SRCD_CD_USER_OFFSET;
+
+	regmap_update_bits(regmap, REG_SPDIF_SRCD, SRCD_CD_USER, val);
+
+	return 0;
+}
+
+/* FSL SPDIF IEC958 controller defines */
+static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
+	/* status cchanel controller */
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_WRITE |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_info,
+		.get = fsl_spdif_pb_get,
+		.put = fsl_spdif_pb_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_info,
+		.get = fsl_spdif_capture_get,
+	},
+	/* user bits controller */
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = "IEC958 Subcode Capture Default",
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_info,
+		.get = fsl_spdif_subcode_get,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = "IEC958 Q-subcode Capture Default",
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_qinfo,
+		.get = fsl_spdif_qget,
+	},
+	/* valid bit error controller */
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = "IEC958 V-Bit Errors",
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_vbit_info,
+		.get = fsl_spdif_vbit_get,
+	},
+	/* DPLL lock info get controller */
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = "RX Sample Rate",
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_rxrate_info,
+		.get = fsl_spdif_rxrate_get,
+	},
+	/* User bit sync mode set/get controller */
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = "IEC958 USyncMode CDText",
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			SNDRV_CTL_ELEM_ACCESS_WRITE |
+			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.info = fsl_spdif_usync_info,
+		.get = fsl_spdif_usync_get,
+		.put = fsl_spdif_usync_put,
+	},
+};
+
+static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+	struct fsl_spdif_priv *spdif_private = snd_soc_dai_get_drvdata(dai);
+
+	dai->playback_dma_data = &spdif_private->dma_params_tx;
+	dai->capture_dma_data = &spdif_private->dma_params_rx;
+
+	snd_soc_add_dai_controls(dai, fsl_spdif_ctrls, ARRAY_SIZE(fsl_spdif_ctrls));
+
+	return 0;
+}
+
+struct snd_soc_dai_driver fsl_spdif_dai = {
+	.probe = &fsl_spdif_dai_probe,
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = FSL_SPDIF_RATES_PLAYBACK,
+		.formats = FSL_SPDIF_FORMATS_PLAYBACK,
+	},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = FSL_SPDIF_RATES_CAPTURE,
+		.formats = FSL_SPDIF_FORMATS_CAPTURE,
+	},
+	.ops = &fsl_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_spdif_component = {
+	.name		= "fsl-spdif",
+};
+
+/*
+ * ================
+ * FSL SPDIF REGMAP
+ * ================
+ */
+
+static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case REG_SPDIF_SCR:
+	case REG_SPDIF_SRCD:
+	case REG_SPDIF_SRPC:
+	case REG_SPDIF_SIE:
+	case REG_SPDIF_SIS:
+	case REG_SPDIF_SRL:
+	case REG_SPDIF_SRR:
+	case REG_SPDIF_SRCSH:
+	case REG_SPDIF_SRCSL:
+	case REG_SPDIF_SRU:
+	case REG_SPDIF_SRQ:
+	case REG_SPDIF_STCSCH:
+	case REG_SPDIF_STCSCL:
+	case REG_SPDIF_SRFM:
+	case REG_SPDIF_STC:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case REG_SPDIF_SCR:
+	case REG_SPDIF_SRCD:
+	case REG_SPDIF_SRPC:
+	case REG_SPDIF_SIE:
+	case REG_SPDIF_SIC:
+	case REG_SPDIF_STL:
+	case REG_SPDIF_STR:
+	case REG_SPDIF_STCSCH:
+	case REG_SPDIF_STCSCL:
+	case REG_SPDIF_STC:
+		return true;
+	default:
+		return false;
+	};
+}
+
+static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg)
+{
+	/* Sync all registers after reset */
+	return true;
+}
+
+static const struct regmap_config fsl_spdif_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+
+	.max_register = REG_SPDIF_STC,
+	.readable_reg = fsl_spdif_readable_reg,
+	.writeable_reg = fsl_spdif_writeable_reg,
+	.volatile_reg = fsl_spdif_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static void spdif_clk_cal_txdiv(struct fsl_spdif_priv *spdif_priv)
+{
+	struct platform_device *pdev = spdif_priv->pdev;
+	struct clk **clk = spdif_priv->txclk;
+	u64 rate_ideal, rate_actual, sub, savesub;
+	u32 i, div, arate, rate[] = {32000, 44100, 48000};
+
+	for (i = 0; i < SPDIF_TXRATE_MAX; i++, savesub = 100000) {
+		for (div = 1; div <= 128; div++) {
+			rate_ideal = rate[i] * (div + 1) * 64;
+			rate_actual = clk_round_rate(clk[i], rate_ideal);
+
+			arate = rate_actual / 64;
+			arate /= div;
+			if (arate == rate[i]) {
+				savesub = 0;
+				spdif_priv->txclk_div[i] = div;
+				break;
+			} else if (arate / rate[i] == 1) {
+				sub = (arate - rate[i]) * 100000;
+				do_div(sub, rate[i]);
+				if (sub < savesub) {
+					savesub = sub;
+					spdif_priv->txclk_div[i] = div;
+				}
+			} else if (rate[i] / arate == 1) {
+				sub = (rate[i] - arate) * 100000;
+				do_div(sub, rate[i]);
+				if (sub < savesub) {
+					savesub = sub;
+					spdif_priv->txclk_div[i] = div;
+				}
+			}
+		}
+		dev_dbg(&pdev->dev, "calculated %dHz div: %d\n",
+				rate[i], spdif_priv->txclk_div[i]);
+	}
+}
+
+static int fsl_spdif_probe(struct platform_device *pdev)
+{
+	const char *p, *rate[] = {"tx-32000", "tx-44100", "tx-48000"};
+	struct fsl_spdif_priv *spdif_priv;
+	struct spdif_mixer_control *ctrl;
+	struct device_node *np = pdev->dev.of_node;
+	struct resource res;
+	void __iomem *regs;
+	int ret = 0, count, i, j;
+	bool lock = false;
+	char *name;
+
+	if (!of_device_is_available(np))
+		return -ENODEV;
+
+	/* The DAI name is the last part of the full name of the node. */
+	p = strrchr(np->full_name, '/') + 1;
+	spdif_priv = devm_kzalloc(&pdev->dev,
+			sizeof(struct fsl_spdif_priv) + strlen(p) + 1, GFP_KERNEL);
+	if (!spdif_priv) {
+		dev_err(&pdev->dev, "could not allocate DAI object\n");
+		return -ENOMEM;
+	}
+
+	strcpy(spdif_priv->name, p);
+
+	spdif_priv->pdev = pdev;
+
+	/* Initialize this copy of the CPU DAI driver structure */
+	memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
+	spdif_priv->cpu_dai_drv.name = spdif_priv->name;
+
+	/* Get the addresses and IRQ */
+	ret = of_address_to_resource(np, 0, &res);
+	if (ret) {
+		dev_err(&pdev->dev, "could not determine device resources\n");
+		return ret;
+	}
+
+	regs = of_iomap(np, 0);
+	if (IS_ERR(regs)) {
+		dev_err(&pdev->dev, "could not map device resources\n");
+		return PTR_ERR(regs);
+	}
+
+	spdif_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+			"core", regs, &fsl_spdif_regmap_config);
+	if (IS_ERR(spdif_priv->regmap)) {
+		dev_err(&pdev->dev, "regmap init failed\n");
+		ret = PTR_ERR(spdif_priv->regmap);
+		return ret;
+	}
+
+	spdif_priv->irq = irq_of_parse_and_map(np, 0);
+	if (spdif_priv->irq == NO_IRQ) {
+		dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
+		ret = -ENXIO;
+		goto error_iomap;
+	}
+
+	/* The 'name' should not have any slashes in it. */
+	ret = request_irq(spdif_priv->irq, spdif_isr, 0,
+			spdif_priv->name, spdif_priv);
+	if (ret) {
+		dev_err(&pdev->dev, "could not claim irq %u\n", spdif_priv->irq);
+		goto error_irqmap;
+	}
+
+	/* We use regmap to control the coreclk, so no need to prepare it */
+	spdif_priv->coreclk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(spdif_priv->coreclk)) {
+		ret = PTR_ERR(spdif_priv->coreclk);
+		dev_err(&pdev->dev, "failed to get clock: %d\n", ret);
+		goto error_irqreq;
+	}
+
+	spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rx");
+	if (IS_ERR(spdif_priv->rxclk)) {
+		/* Use coreclk as default rxclk */
+		spdif_priv->rxclk = spdif_priv->coreclk;
+		dev_warn(&pdev->dev, "using core clock as rx clk\n");
+	} else {
+		clk_prepare(spdif_priv->rxclk);
+	}
+
+	spdif_priv->txclk_main = devm_clk_get(&pdev->dev, "tx");
+	if (IS_ERR(spdif_priv->txclk_main)) {
+		/* Use coreclk as default txclk */
+		spdif_priv->txclk_main = spdif_priv->coreclk;
+		dev_warn(&pdev->dev, "using core clock as tx clk\n");
+	} else {
+		clk_prepare(spdif_priv->txclk_main);
+	}
+
+	for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+		spdif_priv->txclk[i] = devm_clk_get(&pdev->dev, rate[i]);
+		if (IS_ERR(spdif_priv->txclk[i])) {
+			/* Use txclk as default clksrc for 32000 sample rate */
+			spdif_priv->txclk[i] = spdif_priv->txclk_main;
+			dev_warn(&pdev->dev, "using tx clock as %s clk\n", rate[i]);
+		} else {
+			clk_prepare(spdif_priv->txclk[i]);
+		}
+	}
+
+	count = of_property_count_strings(np, "tx-clksrc-names");
+	if (!count) {
+		dev_err(&pdev->dev, "no tx clksrc name registered\n");
+		goto error_clk;
+	} else if (count > STC_CLK_MAX) {
+		dev_err(&pdev->dev, "too many tx clksrc names registered\n");
+		goto error_clk;
+	}
+
+	for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+		spdif_priv->txclk_src[i] = DEFAULT_TXCLK_SRC;
+
+	for (i = 0; i < count; i++) {
+		if (of_property_read_string_index(np, "tx-clksrc-names",
+					i, (const char **)&name))
+			continue;
+		if (!strncmp(name, "lock.", strlen("lock."))) {
+			tx_clklist[i].lock = true;
+			name = strstr(name, ".") + 1;
+		}
+
+		strncpy(tx_clklist[i].name, name, 16);
+
+		for (j = 0; j < SPDIF_TXRATE_MAX; j++) {
+			if (!strcmp(name, __clk_get_name(spdif_priv->txclk[j])))
+				spdif_priv->txclk_src[j] = i;
+		}
+	}
+
+	if (of_property_read_bool(np, "rx-clksrc-lock"))
+		lock = true;
+
+	count = of_property_count_strings(np, "rx-clksrc-names");
+	if (!count) {
+		dev_err(&pdev->dev, "no rx clksrc name registered\n");
+		goto error_clk;
+	} else if (count > SRPC_CLK_MAX) {
+		dev_err(&pdev->dev, "too many rx clksrc names registered\n");
+		goto error_clk;
+	}
+
+	spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC;
+	for (i = 0; i < count; i++) {
+		if (of_property_read_string_index(np, "rx-clksrc-names",
+					i, (const char **)&name))
+			continue;
+		if (!strncmp(name, "lock.", strlen("lock."))) {
+			rx_clklist[i].lock = true;
+			name = strstr(name, ".") + 1;
+		}
+
+		strncpy(rx_clklist[i].name, name, 16);
+
+		if (!strcmp(name, __clk_get_name(spdif_priv->rxclk))
+				&& !(rx_clklist[i].lock ^ lock))
+				spdif_priv->rxclk_src = i;
+	}
+
+	spdif_clk_cal_txdiv(spdif_priv);
+
+	ctrl = &spdif_priv->fsl_spdif_control;
+	/* initial spinlock for control data */
+	spin_lock_init(&ctrl->ctl_lock);
+
+	/* init tx channel status default value */
+	ctrl->ch_status[0] =
+		IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_5015;
+	ctrl->ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID;
+	ctrl->ch_status[2] = 0x00;
+	ctrl->ch_status[3] =
+		IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM;
+
+	atomic_set(&spdif_priv->dpll_locked, 0);
+
+	spdif_priv->dma_params_tx.maxburst = FSL_SPDIF_TXFIFO_WML;
+	spdif_priv->dma_params_rx.maxburst = FSL_SPDIF_RXFIFO_WML;
+	spdif_priv->dma_params_tx.addr = res.start + REG_SPDIF_STL;
+	spdif_priv->dma_params_rx.addr = res.start + REG_SPDIF_SRL;
+
+	/* Register with ASoC */
+	dev_set_drvdata(&pdev->dev, spdif_priv);
+
+	ret = snd_soc_register_component(&pdev->dev, &fsl_spdif_component,
+					 &spdif_priv->cpu_dai_drv, 1);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
+		goto error_dev;
+	}
+
+	ret = imx_pcm_dma_init(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret);
+		goto error_component;
+	}
+
+	return ret;
+
+error_component:
+	snd_soc_unregister_component(&pdev->dev);
+error_dev:
+	dev_set_drvdata(&pdev->dev, NULL);
+error_clk:
+	for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+		if (spdif_priv->txclk[i] != spdif_priv->txclk_main)
+			clk_unprepare(spdif_priv->txclk[i]);
+	}
+	if (spdif_priv->txclk_main != spdif_priv->coreclk)
+		clk_unprepare(spdif_priv->txclk_main);
+	if (spdif_priv->rxclk != spdif_priv->coreclk)
+		clk_unprepare(spdif_priv->rxclk);
+error_irqreq:
+	free_irq(spdif_priv->irq, spdif_priv);
+error_irqmap:
+	irq_dispose_mapping(spdif_priv->irq);
+error_iomap:
+	iounmap(regs);
+
+	return ret;
+}
+
+static int fsl_spdif_remove(struct platform_device *pdev)
+{
+	struct fsl_spdif_priv *spdif_priv = platform_get_drvdata(pdev);
+	int i;
+
+	imx_pcm_dma_exit(pdev);
+	snd_soc_unregister_component(&pdev->dev);
+
+	for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+		if (spdif_priv->txclk[i] != spdif_priv->txclk_main)
+			clk_unprepare(spdif_priv->txclk[i]);
+	}
+	if (spdif_priv->txclk_main != spdif_priv->coreclk)
+		clk_unprepare(spdif_priv->txclk_main);
+	if (spdif_priv->rxclk != spdif_priv->coreclk)
+		clk_unprepare(spdif_priv->rxclk);
+
+	free_irq(spdif_priv->irq, spdif_priv);
+	irq_dispose_mapping(spdif_priv->irq);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id fsl_spdif_dt_ids[] = {
+	{ .compatible = "fsl,fsl-spdif", },
+	{ .compatible = "fsl,imx6q-spdif", },
+	{ .compatible = "fsl,imx6sl-spdif", },
+	{ .compatible = "fsl,imx53-spdif", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
+
+static struct platform_driver fsl_spdif_driver = {
+	.driver = {
+		.name = "fsl-spdif-dai",
+		.owner = THIS_MODULE,
+		.of_match_table = fsl_spdif_dt_ids,
+	},
+	.probe = fsl_spdif_probe,
+	.remove = fsl_spdif_remove,
+};
+
+module_platform_driver(fsl_spdif_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale S/PDIF CPU DAI Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:fsl_spdif");
diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h
new file mode 100644
index 0000000..feacc3a
--- /dev/null
+++ b/sound/soc/fsl/fsl_spdif.h
@@ -0,0 +1,223 @@
+/*
+ * fsl_spdif.h - ALSA S/PDIF interface for the Freescale i.MX SoC
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <b42378@freescale.com>
+ *
+ * Based on fsl_ssi.h
+ * Author: Timur Tabi <timur@freescale.com>
+ * Copyright 2007-2008 Freescale Semiconductor, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program  is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _FSL_SPDIF_DAI_H
+#define _FSL_SPDIF_DAI_H
+
+/* S/PDIF Register Map */
+#define REG_SPDIF_SCR 			0x0	/* SPDIF Configuration Register */
+#define REG_SPDIF_SRCD		 	0x4	/* CDText Control Register */
+#define REG_SPDIF_SRPC			0x8	/* PhaseConfig Register */
+#define REG_SPDIF_SIE			0xc	/* InterruptEn Register */
+#define REG_SPDIF_SIS			0x10	/* InterruptStat Register */
+#define REG_SPDIF_SIC			0x10	/* InterruptClear Register */
+#define REG_SPDIF_SRL			0x14	/* SPDIFRxLeft Register */
+#define REG_SPDIF_SRR			0x18	/* SPDIFRxRight Register */
+#define REG_SPDIF_SRCSH			0x1c	/* SPDIFRxCChannel_h Register */
+#define REG_SPDIF_SRCSL			0x20	/* SPDIFRxCChannel_l Register */
+#define REG_SPDIF_SRU			0x24	/* UchannelRx Register */
+#define REG_SPDIF_SRQ			0x28	/* QchannelRx Register */
+#define REG_SPDIF_STL			0x2C	/* SPDIFTxLeft Register */
+#define REG_SPDIF_STR			0x30	/* SPDIFTxRight Register */
+#define REG_SPDIF_STCSCH		0x34	/* SPDIFTxCChannelCons_h Register */
+#define REG_SPDIF_STCSCL		0x38	/* SPDIFTxCChannelCons_l Register */
+#define REG_SPDIF_SRFM			0x44	/* FreqMeas Register */
+#define REG_SPDIF_STC			0x50	/* SPDIFTxClk Register */
+
+
+/* SPDIF Configuration register */
+#define SCR_RXFIFO_CTL_OFFSET		23
+#define SCR_RXFIFO_CTL_MASK		(1 << SCR_RXFIFO_CTL_OFFSET)
+#define SCR_RXFIFO_CTL_ZERO		(1 << SCR_RXFIFO_CTL_OFFSET)
+#define SCR_RXFIFO_OFF_OFFSET		22
+#define SCR_RXFIFO_OFF_MASK		(1 << SCR_RXFIFO_OFF_OFFSET)
+#define SCR_RXFIFO_OFF			(1 << SCR_RXFIFO_OFF_OFFSET)
+#define SCR_RXFIFO_RST_OFFSET		21
+#define SCR_RXFIFO_RST_MASK		(1 << SCR_RXFIFO_RST_OFFSET)
+#define SCR_RXFIFO_RST			(1 << SCR_RXFIFO_RST_OFFSET)
+#define SCR_RXFIFO_FSEL_OFFSET		19
+#define SCR_RXFIFO_FSEL_MASK		(0x3 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF0		(0x0 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF4		(0x1 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF8		(0x2 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF12		(0x3 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_AUTOSYNC_OFFSET	18
+#define SCR_RXFIFO_AUTOSYNC_MASK	(1 << SCR_RXFIFO_AUTOSYNC_OFFSET)
+#define SCR_RXFIFO_AUTOSYNC		(1 << SCR_RXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_AUTOSYNC_OFFSET	17
+#define SCR_TXFIFO_AUTOSYNC_MASK	(1 << SCR_TXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_AUTOSYNC		(1 << SCR_TXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_FSEL_OFFSET		15
+#define SCR_TXFIFO_FSEL_MASK		(0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF0		(0x0 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF4		(0x1 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF8		(0x2 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF12		(0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_LOW_POWER			(1 << 13)
+#define SCR_SOFT_RESET			(1 << 12)
+#define SCR_TXFIFO_CTRL_OFFSET		10
+#define SCR_TXFIFO_CTRL_MASK		(0x3 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_ZERO		(0x0 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_NORMAL		(0x1 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_ONESAMPLE	(0x2 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_DMA_RX_EN_OFFSET		9
+#define SCR_DMA_RX_EN_MASK		(1 << SCR_DMA_RX_EN_OFFSET)
+#define SCR_DMA_RX_EN			(1 << SCR_DMA_RX_EN_OFFSET)
+#define SCR_DMA_TX_EN_OFFSET		8
+#define SCR_DMA_TX_EN_MASK		(1 << SCR_DMA_TX_EN_OFFSET)
+#define SCR_DMA_TX_EN			(1 << SCR_DMA_TX_EN_OFFSET)
+#define SCR_VAL_OFFSET			5
+#define SCR_VAL_MASK			(1 << SCR_VAL_OFFSET)
+#define SCR_VAL_CLEAR			(1 << SCR_VAL_OFFSET)
+#define SCR_TXSEL_OFFSET		2
+#define SCR_TXSEL_MASK			(0x7 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_OFF			(0 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_RX			(1 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_NORMAL		(0x5 << SCR_TXSEL_OFFSET)
+#define SCR_USRC_SEL_OFFSET		0x0
+#define SCR_USRC_SEL_MASK		(0x3 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_NONE		(0x0 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_RECV		(0x1 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_CHIP		(0x3 << SCR_USRC_SEL_OFFSET)
+
+/* SPDIF CDText control */
+#define SRCD_CD_USER_OFFSET		1
+#define SRCD_CD_USER			(1 << SRCD_CD_USER_OFFSET)
+
+/* SPDIF Phase Configuration register */
+#define SRPC_DPLL_LOCKED		(1 << 6)
+#define SRPC_CLKSRC_SEL_OFFSET		7
+#define SRPC_CLKSRC_SEL_MASK		(0xf << SRPC_CLKSRC_SEL_OFFSET)
+#define SRPC_CLKSRC_SEL_SET(x)		((x << SRPC_CLKSRC_SEL_OFFSET) & SRPC_CLKSRC_SEL_MASK)
+#define SRPC_CLKSRC_SEL_LOCKED		5
+#define SRPC_GAINSEL_OFFSET		3
+#define SRPC_GAINSEL_MASK		(0x7 << SRPC_GAINSEL_OFFSET)
+#define SRPC_GAINSEL_SET(x)		((x << SRPC_GAINSEL_OFFSET) & SRPC_GAINSEL_MASK)
+
+/* SPDIF rx clock source */
+enum spdif_rxclk_src {
+	SRPC_CLKSRC_0 = 0,
+	SRPC_CLKSRC_1,
+	SRPC_CLKSRC_2,
+	SRPC_CLKSRC_3,
+	SRPC_CLKSRC_4,
+	SRPC_CLKSRC_5,
+	SRPC_CLKSRC_6,
+	SRPC_CLKSRC_7,
+	SRPC_CLKSRC_8,
+	SRPC_CLKSRC_9,
+	SRPC_CLKSRC_10,
+	SRPC_CLKSRC_11,
+	SRPC_CLKSRC_12,
+	SRPC_CLKSRC_13,
+	SRPC_CLKSRC_14,
+	SRPC_CLKSRC_15,
+};
+#define SRPC_CLKSRC_MAX			(SRPC_CLKSRC_15 + 1)
+#define DEFAULT_RXCLK_SRC		SRPC_CLKSRC_0
+
+enum spdif_gainsel {
+	GAINSEL_MULTI_24 = 0,
+	GAINSEL_MULTI_16,
+	GAINSEL_MULTI_12,
+	GAINSEL_MULTI_8,
+	GAINSEL_MULTI_6,
+	GAINSEL_MULTI_4,
+	GAINSEL_MULTI_3,
+};
+#define GAINSEL_MULTI_MAX		(GAINSEL_MULTI_3 + 1)
+#define SPDIF_DEFAULT_GAINSEL		GAINSEL_MULTI_8
+
+/* SPDIF interrupt mask define */
+#define INT_DPLL_LOCKED			(1 << 20)
+#define INT_TXFIFO_UNOV			(1 << 19)
+#define INT_TXFIFO_RESYNC		(1 << 18)
+#define INT_CNEW			(1 << 17)
+#define INT_VAL_NOGOOD			(1 << 16)
+#define INT_SYM_ERR			(1 << 15)
+#define INT_BIT_ERR			(1 << 14)
+#define INT_URX_FUL			(1 << 10)
+#define INT_URX_OV			(1 << 9)
+#define INT_QRX_FUL			(1 << 8)
+#define INT_QRX_OV			(1 << 7)
+#define INT_UQ_SYNC			(1 << 6)
+#define INT_UQ_ERR			(1 << 5)
+#define INT_RXFIFO_UNOV			(1 << 4)
+#define INT_RXFIFO_RESYNC		(1 << 3)
+#define INT_LOSS_LOCK			(1 << 2)
+#define INT_TX_EM			(1 << 1)
+#define INT_RXFIFO_FUL			(1 << 0)
+
+/* SPDIF Clock register */
+#define STC_SYSCLK_DIV_OFFSET		11
+#define STC_SYSCLK_DIV_MASK		(0x1ff << STC_TXCLK_SRC_OFFSET)
+#define STC_SYSCLK_DIV(x)		((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK)
+#define STC_TXCLK_SRC_OFFSET		8
+#define STC_TXCLK_SRC_MASK		(0x7 << STC_TXCLK_SRC_OFFSET)
+#define STC_TXCLK_SRC_SET(x)		((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK)
+#define STC_TXCLK_ALL_EN_OFFSET		7
+#define STC_TXCLK_ALL_EN_MASK		(1 << STC_TXCLK_ALL_EN_OFFSET)
+#define STC_TXCLK_ALL_EN		(1 << STC_TXCLK_ALL_EN_OFFSET)
+#define STC_TXCLK_DIV_OFFSET		0
+#define STC_TXCLK_DIV_MASK		(0x7ff << STC_TXCLK_DIV_OFFSET)
+#define STC_TXCLK_DIV(x)		((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK)
+
+/* SPDIF tx clksrc */
+enum spdif_txclk_src {
+	STC_TXCLK_SRC_0 = 0,
+	STC_TXCLK_SRC_1,
+	STC_TXCLK_SRC_2,
+	STC_TXCLK_SRC_3,
+	STC_TXCLK_SRC_4,
+	STC_TXCLK_SRC_5,
+	STC_TXCLK_SRC_6,
+	STC_TXCLK_SRC_7,
+};
+#define STC_TXCLK_SRC_MAX		(STC_TXCLK_SRC_7 + 1)
+#define DEFAULT_TXCLK_SRC		STC_TXCLK_SRC_1
+
+/* SPDIF tx rate */
+enum spdif_txrate {
+	SPDIF_TXRATE_32000 = 0,
+	SPDIF_TXRATE_44100,
+	SPDIF_TXRATE_48000,
+};
+#define SPDIF_TXRATE_MAX		(SPDIF_TXRATE_48000 + 1)
+
+
+#define SPDIF_CSTATUS_BYTE		6
+#define SPDIF_UBITS_SIZE		96
+#define SPDIF_QSUB_SIZE			(SPDIF_UBITS_SIZE / 8)
+
+
+#define FSL_SPDIF_RATES_PLAYBACK	(SNDRV_PCM_RATE_32000 |	\
+					 SNDRV_PCM_RATE_44100 |	\
+					 SNDRV_PCM_RATE_48000)
+
+#define FSL_SPDIF_RATES_CAPTURE		(SNDRV_PCM_RATE_16000 | \
+					 SNDRV_PCM_RATE_32000 |	\
+					 SNDRV_PCM_RATE_44100 | \
+					 SNDRV_PCM_RATE_48000 |	\
+					 SNDRV_PCM_RATE_64000 | \
+					 SNDRV_PCM_RATE_96000)
+
+#define FSL_SPDIF_FORMATS_PLAYBACK	(SNDRV_PCM_FMTBIT_S16_LE | \
+					 SNDRV_PCM_FMTBIT_S20_3LE | \
+					 SNDRV_PCM_FMTBIT_S24_LE)
+
+#define FSL_SPDIF_FORMATS_CAPTURE	(SNDRV_PCM_FMTBIT_S24_LE)
+
+#endif /* _FSL_SPDIF_DAI_H */
-- 
1.7.1

^ permalink raw reply related

* [PATCH v4 2/2] ASoC: fsl: Add S/PDIF machine driver
From: Nicolin Chen @ 2013-08-12 12:01 UTC (permalink / raw)
  To: broonie, lars, p.zabel
  Cc: devicetree, alsa-devel, linuxppc-dev, timur, rob.herring
In-Reply-To: <1376308870-14232-1-git-send-email-b42378@freescale.com>

Add S/PDIF machine driver for Freescale i.MX series SoC.

Signed-off-by: Nicolin Chen <b42378@freescale.com>
---
 .../devicetree/bindings/sound/imx-audio-spdif.txt  |   29 +++++
 sound/soc/fsl/Kconfig                              |   11 ++
 sound/soc/fsl/Makefile                             |    2 +
 sound/soc/fsl/imx-spdif.c                          |  134 ++++++++++++++++++++
 4 files changed, 176 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
 create mode 100644 sound/soc/fsl/imx-spdif.c

diff --git a/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
new file mode 100644
index 0000000..9a3fa26
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
@@ -0,0 +1,29 @@
+Freescale i.MX audio complex with S/PDIF transceiver
+
+Required properties:
+
+  - compatible : "fsl,imx-audio-spdif"
+
+  - model : The user-visible name of this sound complex
+
+  - spdif-controller : The phandle of the i.MX S/PDIF controller
+
+
+Optional properties:
+
+  - spdif-transmitter : The phandle of the spdif-transmitter dummy codec
+
+  - spdif-receiver : The phandle of the spdif-receiver dummy codec
+
+* Note: At least one of these two properties should be set in the DT binding.
+
+
+Example:
+
+sound-spdif {
+	compatible = "fsl,imx-audio-spdif";
+	model = "imx-spdif";
+	spdif-controller = <&spdif>;
+	spdif-transmitter = <&spdif_tx_codec>;
+	spdif-receiver = <&spdif_rx_codec>;
+};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 2c518db..4cc118c 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -195,6 +195,17 @@ config SND_SOC_IMX_SGTL5000
 	  Say Y if you want to add support for SoC audio on an i.MX board with
 	  a sgtl5000 codec.
 
+config SND_SOC_IMX_SPDIF
+	tristate "SoC Audio support for i.MX boards with S/PDIF"
+	select SND_SOC_IMX_PCM_DMA
+	select SND_SOC_FSL_SPDIF
+	select SND_SOC_FSL_UTILS
+	select SND_SOC_SPDIF
+	help
+	  SoC Audio support for i.MX boards with S/PDIF
+	  Say Y if you want to add support for SoC audio on an i.MX board with
+	  a S/DPDIF.
+
 config SND_SOC_IMX_MC13783
 	tristate "SoC Audio support for I.MX boards with mc13783"
 	depends on MFD_MC13783 && ARM
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 4b5970e..e2aaff7 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -45,6 +45,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
 snd-soc-wm1133-ev1-objs := wm1133-ev1.o
 snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
 snd-soc-imx-wm8962-objs := imx-wm8962.o
+snd-soc-imx-spdif-objs :=imx-spdif.o
 snd-soc-imx-mc13783-objs := imx-mc13783.o
 
 obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
@@ -53,4 +54,5 @@ obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
 obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
 obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
 obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
+obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
 obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
new file mode 100644
index 0000000..893f3d1
--- /dev/null
+++ b/sound/soc/fsl/imx-spdif.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/soc.h>
+
+struct imx_spdif_data {
+	struct snd_soc_dai_link dai[2];
+	struct snd_soc_card card;
+};
+
+static int imx_spdif_audio_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *spdif_np, *codec_tx_np, *codec_rx_np;
+	struct platform_device *spdif_pdev;
+	struct imx_spdif_data *data;
+	int ret = 0, num_links = 0;
+
+	spdif_np = of_parse_phandle(np, "spdif-controller", 0);
+	if (!spdif_np) {
+		dev_err(&pdev->dev, "failed to find spdif-controller\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	spdif_pdev = of_find_device_by_node(spdif_np);
+	if (!spdif_pdev) {
+		dev_err(&pdev->dev, "failed to find S/PDIF device\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	codec_tx_np = of_parse_phandle(np, "spdif-transmitter", 0);
+	if (codec_tx_np) {
+		data->dai[num_links].name = "S/PDIF TX";
+		data->dai[num_links].stream_name = "S/PDIF PCM Playback";
+		data->dai[num_links].codec_dai_name = "dit-hifi";
+		data->dai[num_links].codec_of_node = codec_tx_np;
+		data->dai[num_links].cpu_of_node = spdif_np;
+		data->dai[num_links].platform_of_node = spdif_np;
+		num_links++;
+	}
+
+	codec_rx_np = of_parse_phandle(np, "spdif-receiver", 0);
+	if (codec_rx_np) {
+		data->dai[num_links].name = "S/PDIF RX";
+		data->dai[num_links].stream_name = "S/PDIF PCM Capture";
+		data->dai[num_links].codec_dai_name = "dir-hifi";
+		data->dai[num_links].codec_of_node = codec_rx_np;
+		data->dai[num_links].cpu_of_node = spdif_np;
+		data->dai[num_links].platform_of_node = spdif_np;
+		num_links++;
+	}
+
+	if (!num_links) {
+		dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n");
+		goto fail;
+	}
+
+	data->card.dev = &pdev->dev;
+	data->card.num_links = num_links;
+	data->card.dai_link = data->dai;
+
+	ret = snd_soc_of_parse_card_name(&data->card, "model");
+	if (ret)
+		goto fail;
+
+	ret = snd_soc_register_card(&data->card);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+		goto fail;
+	}
+
+	platform_set_drvdata(pdev, data);
+
+fail:
+	if (codec_tx_np)
+		of_node_put(codec_tx_np);
+	if (codec_rx_np)
+		of_node_put(codec_rx_np);
+	if (spdif_np)
+		of_node_put(spdif_np);
+
+	return ret;
+}
+
+static int imx_spdif_audio_remove(struct platform_device *pdev)
+{
+	struct imx_spdif_data *data = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_card(&data->card);
+
+	return 0;
+}
+
+static const struct of_device_id imx_spdif_dt_ids[] = {
+	{ .compatible = "fsl,imx-audio-spdif", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids);
+
+static struct platform_driver imx_spdif_driver = {
+	.driver = {
+		.name = "imx-spdif",
+		.owner = THIS_MODULE,
+		.of_match_table = imx_spdif_dt_ids,
+	},
+	.probe = imx_spdif_audio_probe,
+	.remove = imx_spdif_audio_remove,
+};
+
+module_platform_driver(imx_spdif_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale i.MX S/PDIF machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-spdif");
-- 
1.7.1

^ permalink raw reply related

* Re: mm/slab: ppc: ubi: kmalloc_slab WARNING / PPC + UBI driver
From: Wladislav Wiebe @ 2013-08-12 11:06 UTC (permalink / raw)
  To: Aaro Koskinen
  Cc: dedekind1, dwmw2, penberg, linux-mm, linux-mtd, cl, linuxppc-dev
In-Reply-To: <20130731173434.GA27470@blackmetal.musicnaut.iki.fi>

Hi guys,

we got the real root cause of the allocation issue:

Subject: [PATCH 1/1] of: fdt: fix memory initialization for expanded DT

Already existing property flags are filled wrong for properties created from
initial FDT. This could cause problems if this DYNAMIC device-tree functions
are used later, i.e. properties are attached/detached/replaced. Simply dumping
flags from the running system show, that some initial static (not allocated via
kzmalloc()) nodes are marked as dynamic.

I putted some debug extensions to property_proc_show(..) :
..
+       if (OF_IS_DYNAMIC(pp))
+               pr_err("DEBUG: xxx : OF_IS_DYNAMIC\n");
+       if (OF_IS_DETACHED(pp))
+               pr_err("DEBUG: xxx : OF_IS_DETACHED\n");

when you operate on the nodes (e.g.: ~$ cat /proc/device-tree/*some_node*) you
will see that those flags are filled wrong, basically in most cases it will dump
a DYNAMIC or DETACHED status, which is in not true.
(BTW. this OF_IS_DETACHED is a own define for debug purposes which which just
make a test_bit(OF_DETACHED, &x->_flags)

If nodes are dynamic kernel is allowed to kfree() them. But it will crash
attempting to do so on the nodes from FDT -- they are not allocated via
kzmalloc().

Signed-off-by: Wladislav Wiebe <wladislav.kw@gmail.com>
---
 drivers/of/fdt.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 6bb7cf2..b10ba00 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -392,6 +392,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
 	mem = (unsigned long)
 		dt_alloc(size + 4, __alignof__(struct device_node));

+	memset((void *)mem, 0, size);
+
 	((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);

 	pr_debug("  unflattening %lx...\n", mem);
-- 1.7.1

This is committed to the mainline - hope it comes in soon.

Thanks & BR,
Wladislav Wiebe


On 31/07/13 19:34, Aaro Koskinen wrote:
> Hi,
> 
> On Wed, Jul 31, 2013 at 01:42:31PM +0200, Wladislav Wiebe wrote:
>> DEBUG: xxx kmalloc_slab, requested 'size' = 8388608, KMALLOC_MAX_SIZE = 4194304
> [...]
>> [ccd3be60] [c0099fd4] kmalloc_slab+0x48/0xe8 (unreliable)
>> [ccd3be70] [c00ae650] __kmalloc+0x20/0x1b4
>> [ccd3be90] [c00d46f4] seq_read+0x2a4/0x540
>> [ccd3bee0] [c00fe09c] proc_reg_read+0x5c/0x90
>> [ccd3bef0] [c00b4e1c] vfs_read+0xa4/0x150
>> [ccd3bf10] [c00b500c] SyS_read+0x4c/0x84
>> [ccd3bf40] [c000be80] ret_from_syscall+0x0/0x3c
> 
> It seems some procfs file is trying to dump 8 MB at a single go. You
> need to fix that to return data in smaller chunks. What file is it?
> 
> A.
> 

^ permalink raw reply related

* Powerpc: Kernel warn_on when enabling IOMMU_API
From: Bhushan Bharat-R65777 @ 2013-08-12 10:20 UTC (permalink / raw)
  To: aik@ozlabs.ru, benh@kernel.crashing.org; +Cc: linuxppc-dev@lists.ozlabs.org

Hi Alexey/Ben,

When I enable the IOMMU_API then I get warn_on in arch/powerpc/kernel/iommu=
.c (here is the code snapshot)
{
1110 static int iommu_add_device(struct device *dev)
1111 {
1112         struct iommu_table *tbl;
1113         int ret =3D 0;
1114=20
1115         if (WARN_ON(dev->iommu_group)) {

^^^^
    This is the point is Warn_on.

1116                 pr_warn("iommu_tce: device %s is already in iommu grou=
p %d, skipping\n",
1117                                 dev_name(dev),
1118                                 iommu_group_id(dev->iommu_group));
1119                 return -EBUSY;
1120         }
}


---------------This is the bootlog with #define DEBUG in iommu.c-----------=
--------------

Using P5040 DS machine description
MMU: Supported page sizes
         4 KB as direct
      4096 KB as direct
     16384 KB as direct
     65536 KB as direct
    262144 KB as direct
   1048576 KB as direct
MMU: Book3E HW tablewalk not supported
Found initrd at 0xc00000002b759000:0xc00000002ffff4ab
bootconsole [udbg0] enabled
CPU maps initialized for 1 thread per core
Starting Linux PPC64 #16 SMP Mon Aug 12 15:22:11 IST 2013
-----------------------------------------------------
ppc64_pft_size                =3D 0x0
physicalMemorySize            =3D 0x200000000
ppc64_caches.dcache_line_size =3D 0x40
ppc64_caches.icache_line_size =3D 0x40
-----------------------------------------------------
Linux version 3.11.0-rc1-10505-g8d33668-dirty (r65777@perfidc-01) (gcc vers=
ion 4.5.1 (Sourcery G++ Lite 2010.09-55) ) #16 SMP Mon Aug 12 15:22:11 IST =
2013
CF000012

Setup Arch
[boot]0012 Setup Arch
P5040 DS board from Freescale Semiconductor
Zone ranges:
  DMA      [mem 0x00000000-0x1ffffffff]
  Normal   empty
Movable zone start for each node
Early memory node ranges
  node   0: [mem 0x00000000-0x1ffffffff]
MMU: Allocated 2112 bytes of context maps for 255 contexts
CF000015

Setup Done
[boot]0015 Setup Done
PERCPU: Embedded 10 pages/cpu @c00000000b100000 s11200 r0 d29760 u262144
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 206848=
0
Kernel command line: console=3DttyS0,115200 ramdisk_size=3D10000000 root=3D=
/dev/ram rw
PID hash table entries: 4096 (order: 3, 32768 bytes)
Dentry cache hash table entries: 1048576 (order: 11, 8388608 bytes)
Inode-cache hash table entries: 524288 (order: 10, 4194304 bytes)
Sorting __ex_table...
Memory: 8110276K/8388608K available (6276K kernel code, 1104K rwdata, 2212K=
 rodata, 268K init, 325K bss, 278332K reserved)
SLUB: HWalign=3D64, Order=3D0-3, MinObjects=3D0, CPUs=3D4, Nodes=3D1
Hierarchical RCU implementation.
	RCU restricting CPUs from NR_CPUS=3D24 to nr_cpu_ids=3D4.
NR_IRQS:512 nr_irqs:512 16
mpic: Setting up MPIC " OpenPIC  " version 1.2 at ffe040000, max 4 CPUs
mpic: ISU size: 512, shift: 9, mask: 1ff
mpic: Initializing for 512 sources
clocksource: timebase mult[14000000] shift[24] registered
Console: colour dummy device 80x25
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 256
mpic: requesting IPIs...
Brought up 4 CPUs
devtmpfs: initialized
NET: Registered protocol family 16
Found FSL PCI host bridge at 0x0000000ffe200000. Firmware bus number: 0->1
PCI host bridge /pcie@ffe200000 (primary) ranges:
 MEM 0x0000000c00000000..0x0000000c1fffffff -> 0x00000000e0000000=20
  IO 0x0000000ff8000000..0x0000000ff800ffff -> 0x0000000000000000
/pcie@ffe200000: PCICSRBAR @ 0xdf000000
/pcie@ffe200000: Setup 64-bit PCI DMA window
/pcie@ffe200000: DMA window size is 0xdf000000
Found FSL PCI host bridge at 0x0000000ffe201000. Firmware bus number: 0->1
PCI host bridge /pcie@ffe201000  ranges:
 MEM 0x0000000c20000000..0x0000000c3fffffff -> 0x00000000e0000000=20
  IO 0x0000000ff8010000..0x0000000ff801ffff -> 0x0000000000000000
/pcie@ffe201000: PCICSRBAR @ 0xdf000000
/pcie@ffe201000: Setup 64-bit PCI DMA window
/pcie@ffe201000: DMA window size is 0xdf000000
software IO TLB [mem 0x0bdca000-0x0fdca000] (64MB) mapped at [c00000000bdca=
000-c00000000fdc9fff]
PCI: Probing PCI hardware
fsl-pci ffe200000.pcie: PCI host bridge to bus 0000:00
pci_bus 0000:00: root bus resource [io  0x10000-0x1ffff] (bus address [0x00=
00-0xffff])
pci_bus 0000:00: root bus resource [mem 0xc00000000-0xc1fffffff] (bus addre=
ss [0xe0000000-0xffffffff])
pci_bus 0000:00: root bus resource [bus 00-01]
pci 0000:00:00.0: ignoring class 0x0b2000 (doesn't match header type 01)
pci 0000:00:00.0: PCI bridge to [bus 01-ff]
fsl-pci ffe201000.pcie: PCI host bridge to bus 0001:00
pci_bus 0001:00: root bus resource [io  0x21000-0x30fff] (bus address [0x00=
00-0xffff])
pci_bus 0001:00: root bus resource [mem 0xc20000000-0xc3fffffff] (bus addre=
ss [0xe0000000-0xffffffff])
pci_bus 0001:00: root bus resource [bus 00-01]
pci 0001:00:00.0: ignoring class 0x0b2000 (doesn't match header type 01)
pci 0001:00:00.0: PCI bridge to [bus 01-ff]
pci 0000:00:00.0: PCI bridge to [bus 01]
pci 0000:00:00.0:   bridge window [io  0x10000-0x1ffff]
pci 0000:00:00.0:   bridge window [mem 0xc00000000-0xc1fffffff]
pci 0001:00:00.0: BAR 9: can't assign mem pref (size 0x100000)
pci 0001:01:00.0: BAR 6: assigned [mem 0xc20040000-0xc2007ffff pref]
pci 0001:00:00.0: PCI bridge to [bus 01]
pci 0001:00:00.0:   bridge window [io  0x21000-0x30fff]
pci 0001:00:00.0:   bridge window [mem 0xc20000000-0xc3fffffff]
Some PCI device resources are unassigned, try booting with pci=3Drealloc
bio: create slab <bio-0> at 0
Freescale Elo / Elo Plus DMA driver
fsl-elo-dma ffe100300.dma: #0 (fsl,eloplus-dma-channel), irq 28
fsl-elo-dma ffe100300.dma: #1 (fsl,eloplus-dma-channel), irq 29
fsl-elo-dma ffe100300.dma: #2 (fsl,eloplus-dma-channel), irq 30
fsl-elo-dma ffe100300.dma: #3 (fsl,eloplus-dma-channel), irq 31
fsl-elo-dma ffe101300.dma: #0 (fsl,eloplus-dma-channel), irq 32
fsl-elo-dma ffe101300.dma: #1 (fsl,eloplus-dma-channel), irq 33
fsl-elo-dma ffe101300.dma: #2 (fsl,eloplus-dma-channel), irq 34
fsl-elo-dma ffe101300.dma: #3 (fsl,eloplus-dma-channel), irq 35
vgaarb: loaded
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
pps_core: LinuxPPS API ver. 1 registered
pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giome=
tti@linux.it>
PTP clock support registered
EDAC MC: Ver: 3.0.0
------------[ cut here ]------------
WARNING: at arch/powerpc/kernel/iommu.c:1115
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.11.0-rc1-10505-g8d33668-dirty #=
16
task: c0000001fe080000 ti: c0000001fe07c000 task.ti: c0000001fe07c000
NIP: c00000000001b538 LR: c000000000858210 CTR: c000000000316b70
REGS: c0000001fe07f880 TRAP: 0700   Not tainted  (3.11.0-rc1-10505-g8d33668=
-dirty)
MSR: 0000000080029000 <CE,EE,ME>  CR: 84adbe82  XER: 00000000
SOFTE: 1

GPR00: 0000000000000001 c0000001fe07fb00 c000000000995fb8 c0000001fe26cc00=
=20
GPR04: 0000000000000000 0000000000000000 c000000000955b88 0000000000000000=
=20
GPR08: c000000000892ce8 0000000000000001 000000000a870000 0000000000000001=
=20
GPR12: 0000000024adbe82 c00000000fffa000 c000000000001b30 0000000000000000=
=20
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR24: 0000000000000000 c00000000088da28 00000000000000a7 c000000000883ed0=
=20
GPR28: 0000000000000000 c0000001fe00c098 c0000000008fc020 c0000001fe00c000=
=20
NIP [c00000000001b538] .iommu_add_device+0x38/0x110
LR [c000000000858210] .tce_iommu_init+0x24/0x74
Call Trace:
[c0000001fe07fb00] [c000000000316e24] .pci_get_subsys+0x34/0x50 (unreliable=
)
[c0000001fe07fb90] [c000000000858210] .tce_iommu_init+0x24/0x74
[c0000001fe07fc10] [c00000000000138c] .do_one_initcall+0x5c/0x1b0
[c0000001fe07fd00] [c000000000851b28] .kernel_init_freeable+0x14c/0x214
[c0000001fe07fdb0] [c000000000001b4c] .kernel_init+0x1c/0x4d0
[c0000001fe07fe30] [c000000000000878] .ret_from_kernel_thread+0x5c/0x64
Instruction dump:
fba1ffe8 fbc1fff0 fbe1fff8 7c7d1b78 ebc28200 f8010010 f821ff71 e8630178=20
7c600074 7800d182 68000001 7c0907b4 <0b000000> 2fa90000 409e0068 eb9d0100=20
---[ end trace 1962e9b02154856d ]---
iommu_tce: device 0000:00:00.0 is already in iommu group 9, skipping
------------[ cut here ]------------
WARNING: at arch/powerpc/kernel/iommu.c:1115
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Tainted: G        W    3.11.0-rc1-10505-g8d33=
668-dirty #16
task: c0000001fe080000 ti: c0000001fe07c000 task.ti: c0000001fe07c000
NIP: c00000000001b538 LR: c000000000858210 CTR: c000000000316b70
REGS: c0000001fe07f880 TRAP: 0700   Tainted: G        W     (3.11.0-rc1-105=
05-g8d33668-dirty)
MSR: 0000000080029000 <CE,EE,ME>  CR: 84adbe24  XER: 00000000
SOFTE: 1

GPR00: 0000000000000001 c0000001fe07fb00 c000000000995fb8 c0000001fe26cc00=
=20
GPR04: 0000000000000000 c0000001fe276200 c000000000955b88 c0000001fe00c000=
=20
GPR08: 20392c20736b6970 0000000000000001 c0000000009a3910 0000000000000001=
=20
GPR12: 0000000024adbe24 c00000000fffa000 c000000000001b30 0000000000000000=
=20
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR24: 0000000000000000 c00000000088da28 00000000000000a7 c000000000883ed0=
=20
GPR28: 0000000000000000 c0000001fe00c898 c0000000008fc020 c0000001fe00c800=
=20
NIP [c00000000001b538] .iommu_add_device+0x38/0x110
LR [c000000000858210] .tce_iommu_init+0x24/0x74
Call Trace:
[c0000001fe07fb00] [c000000000316e24] .pci_get_subsys+0x34/0x50 (unreliable=
)
[c0000001fe07fb90] [c000000000858210] .tce_iommu_init+0x24/0x74
[c0000001fe07fc10] [c00000000000138c] .do_one_initcall+0x5c/0x1b0
[c0000001fe07fd00] [c000000000851b28] .kernel_init_freeable+0x14c/0x214
[c0000001fe07fdb0] [c000000000001b4c] .kernel_init+0x1c/0x4d0
[c0000001fe07fe30] [c000000000000878] .ret_from_kernel_thread+0x5c/0x64
Instruction dump:
fba1ffe8 fbc1fff0 fbe1fff8 7c7d1b78 ebc28200 f8010010 f821ff71 e8630178=20
7c600074 7800d182 68000001 7c0907b4 <0b000000> 2fa90000 409e0068 eb9d0100=20
---[ end trace 1962e9b02154856e ]---
iommu_tce: device 0000:01:00.0 is already in iommu group 9, skipping
------------[ cut here ]------------
WARNING: at arch/powerpc/kernel/iommu.c:1115
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Tainted: G        W    3.11.0-rc1-10505-g8d33=
668-dirty #16
task: c0000001fe080000 ti: c0000001fe07c000 task.ti: c0000001fe07c000
NIP: c00000000001b538 LR: c000000000858210 CTR: c000000000316b70
REGS: c0000001fe07f880 TRAP: 0700   Tainted: G        W     (3.11.0-rc1-105=
05-g8d33668-dirty)
MSR: 0000000080029000 <CE,EE,ME>  CR: 82adbe24  XER: 00000000
SOFTE: 1

GPR00: 0000000000000001 c0000001fe07fb00 c000000000995fb8 c0000001fe26cd00=
=20
GPR04: 0000000000000000 c0000001fe276500 c000000000955b88 c0000001fe00c800=
=20
GPR08: 20392c2000000000 0000000000000001 c0000000009a3910 0000000000000001=
=20
GPR12: 0000000022adbe24 c00000000fffa000 c000000000001b30 0000000000000000=
=20
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR24: 0000000000000000 c00000000088da28 00000000000000a7 c000000000883ed0=
=20
GPR28: 0000000000000000 c0000001fe00d098 c0000000008fc020 c0000001fe00d000=
=20
NIP [c00000000001b538] .iommu_add_device+0x38/0x110
LR [c000000000858210] .tce_iommu_init+0x24/0x74
Call Trace:
[c0000001fe07fb00] [c000000000316e24] .pci_get_subsys+0x34/0x50 (unreliable=
)
[c0000001fe07fb90] [c000000000858210] .tce_iommu_init+0x24/0x74
[c0000001fe07fc10] [c00000000000138c] .do_one_initcall+0x5c/0x1b0
[c0000001fe07fd00] [c000000000851b28] .kernel_init_freeable+0x14c/0x214
[c0000001fe07fdb0] [c000000000001b4c] .kernel_init+0x1c/0x4d0
[c0000001fe07fe30] [c000000000000878] .ret_from_kernel_thread+0x5c/0x64
Instruction dump:
fba1ffe8 fbc1fff0 fbe1fff8 7c7d1b78 ebc28200 f8010010 f821ff71 e8630178=20
7c600074 7800d182 68000001 7c0907b4 <0b000000> 2fa90000 409e0068 eb9d0100=20
---[ end trace 1962e9b02154856f ]---
iommu_tce: device 0001:00:00.0 is already in iommu group 7, skipping
------------[ cut here ]------------
WARNING: at arch/powerpc/kernel/iommu.c:1115
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Tainted: G        W    3.11.0-rc1-10505-g8d33=
668-dirty #16
task: c0000001fe080000 ti: c0000001fe07c000 task.ti: c0000001fe07c000
NIP: c00000000001b538 LR: c000000000858210 CTR: c000000000316b70
REGS: c0000001fe07f880 TRAP: 0700   Tainted: G        W     (3.11.0-rc1-105=
05-g8d33668-dirty)
MSR: 0000000080029000 <CE,EE,ME>  CR: 84adbe24  XER: 00000000
SOFTE: 1

GPR00: 0000000000000001 c0000001fe07fb00 c000000000995fb8 c0000001fe26cd00=
=20
GPR04: 0000000000000000 c0000001fe276980 c000000000955b88 c0000001fe00d000=
=20
GPR08: 20372c20736b6970 0000000000000001 c0000000009a3910 0000000000000001=
=20
GPR12: 0000000024adbe24 c00000000fffa000 c000000000001b30 0000000000000000=
=20
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR24: 0000000000000000 c00000000088da28 00000000000000a7 c000000000883ed0=
=20
GPR28: 0000000000000000 c0000001fe00d898 c0000000008fc020 c0000001fe00d800=
=20
NIP [c00000000001b538] .iommu_add_device+0x38/0x110
LR [c000000000858210] .tce_iommu_init+0x24/0x74
Call Trace:
[c0000001fe07fb00] [c000000000316e24] .pci_get_subsys+0x34/0x50 (unreliable=
)
[c0000001fe07fb90] [c000000000858210] .tce_iommu_init+0x24/0x74
[c0000001fe07fc10] [c00000000000138c] .do_one_initcall+0x5c/0x1b0
[c0000001fe07fd00] [c000000000851b28] .kernel_init_freeable+0x14c/0x214
[c0000001fe07fdb0] [c000000000001b4c] .kernel_init+0x1c/0x4d0
[c0000001fe07fe30] [c000000000000878] .ret_from_kernel_thread+0x5c/0x64
Instruction dump:
fba1ffe8 fbc1fff0 fbe1fff8 7c7d1b78 ebc28200 f8010010 f821ff71 e8630178=20
7c600074 7800d182 68000001 7c0907b4 <0b000000> 2fa90000 409e0068 eb9d0100=20
---[ end trace 1962e9b021548570 ]---
iommu_tce: device 0001:01:00.0 is already in iommu group 7, skipping
Switched to clocksource timebase
NET: Registered protocol family 2
TCP established hash table entries: 65536 (order: 8, 1048576 bytes)
TCP bind hash table entries: 65536 (order: 8, 1048576 bytes)
TCP: Hash tables configured (established 65536 bind 65536)
TCP: reno registered
UDP hash table entries: 4096 (order: 5, 131072 bytes)
UDP-Lite hash table entries: 4096 (order: 5, 131072 bytes)
NET: Registered protocol family 1
RPC: Registered named UNIX socket transport module.
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
Trying to unpack rootfs image as initramfs...
rootfs image is not initramfs (no cpio magic); looks like an initrd
Freeing initrd memory: 74392K (c00000002b759000 - c00000002ffff000)
HugeTLB registered 4 MB page size, pre-allocated 0 pages
HugeTLB registered 16 MB page size, pre-allocated 0 pages
HugeTLB registered 64 MB page size, pre-allocated 0 pages
HugeTLB registered 256 MB page size, pre-allocated 0 pages
HugeTLB registered 1 GB page size, pre-allocated 0 pages
NFS: Registering the id_resolver key type
Key type id_resolver registered
Key type id_legacy registered
NTFS driver 2.1.30 [Flags: R/O].
jffs2: version 2.2. (NAND)  2001-2006 Red Hat, Inc.
msgmni has been set to 15985
io scheduler noop registered
io scheduler deadline registered
io scheduler cfq registered (default)
Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
serial8250.0: ttyS0 at MMIO 0xffe11c500 (irq =3D 36) is a 16550A
console [ttyS0] enabled, bootconsole disabled
console [ttyS0] enabled, bootconsole disabled
serial8250.0: ttyS1 at MMIO 0xffe11c600 (irq =3D 36) is a 16550A
serial8250.0: ttyS2 at MMIO 0xffe11d500 (irq =3D 37) is a 16550A
serial8250.0: ttyS3 at MMIO 0xffe11d600 (irq =3D 37) is a 16550A
brd: module loaded
loop: module loaded
fsl-sata ffe220000.sata: Sata FSL Platform/CSB Driver init
scsi0 : sata_fsl
ata1: SATA max UDMA/133 irq 68
fsl-sata ffe221000.sata: Sata FSL Platform/CSB Driver init
scsi1 : sata_fsl
ata2: SATA max UDMA/133 irq 69
fe8000000.flash: Found 1 x16 devices at 0x0 in 16-bit bank. Manufacturer ID=
 0x000001 Chip ID 0x002801
Amd/Fujitsu Extended Query Table at 0x0040
  Amd/Fujitsu Extended Query version 1.3.
number of CFI chips: 1
ftl_cs: FTL header not found.
ONFI param page 0 valid
ONFI flash detected
NAND device: Manufacturer ID: 0x2c, Chip ID: 0xdc (Micron MT29F4G08ABADAWP)=
, 512MiB, page size: 2048, OOB size: 64
Bad block table found at page 262080, version 0x01
Bad block table found at page 262016, version 0x01
6 ofpart partitions found on MTD device ffa00000.flash
Creating 6 MTD partitions on "ffa00000.flash":
0x000000000000-0x000002000000 : "NAND U-Boot Image"
ftl_cs: FTL header not found.
0x000002000000-0x000012000000 : "NAND Root File System"
ftl_cs: FTL header not found.
0x000012000000-0x00001a000000 : "NAND Compressed RFS Image"
ftl_cs: FTL header not found.
0x00001a000000-0x00001e000000 : "NAND Linux Kernel Image"
ftl_cs: FTL header not found.
0x00001e000000-0x00001f000000 : "NAND DTB Image"
ftl_cs: FTL header not found.
0x00001f000000-0x000020000000 : "NAND Writable User area"
ftl_cs: FTL header not found.
eLBC NAND device at 0xfffa00000, bank 2
fsl_espi ffe110000.spi: master is unqueued, this is deprecated
m25p80 spi32766.0: found s25fl129p1, expected s25sl12801
m25p80 spi32766.0: s25fl129p1 (16384 Kbytes)
4 ofpart partitions found on MTD device spi32766.0
Creating 4 MTD partitions on "spi32766.0":
0x000000000000-0x000000100000 : "u-boot"
ftl_cs: FTL header not found.
0x000000100000-0x000000600000 : "kernel"
ftl_cs: FTL header not found.
0x000000600000-0x000000700000 : "dtb"
ftl_cs: FTL header not found.
0x000000700000-0x000001000000 : "file system"
ftl_cs: FTL header not found.
fsl_espi ffe110000.spi: at 0x8000080080058000 (irq =3D 53)
e1000e: Intel(R) PRO/1000 Network Driver - 2.3.2-k
e1000e: Copyright(c) 1999 - 2013 Intel Corporation.
e1000e 0001:01:00.0: Disabling ASPM L0s L1
e1000e 0001:01:00.0: Interrupt Throttling Rate (ints/sec) set to dynamic co=
nservative mode
e1000e 0001:01:00.0 eth0: registered PHC clock
e1000e 0001:01:00.0 eth0: (PCI Express:2.5GT/s:Width x1) 00:1b:21:81:d2:e3
e1000e 0001:01:00.0 eth0: Intel(R) PRO/1000 Network Connection
e1000e 0001:01:00.0 eth0: MAC: 3, PHY: 8, PBA No: E46981-004
VFIO - User Level meta-driver version: 0.3
ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
ehci-pci: EHCI PCI platform driver
fsl-ehci fsl-ehci.0: Freescale On-Chip EHCI Host Controller
fsl-ehci fsl-ehci.0: new USB bus registered, assigned bus number 1
fsl-ehci fsl-ehci.0: irq 44, io mem 0xffe210000
fsl-ehci fsl-ehci.0: USB 2.0 started, EHCI 1.00
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 1 port detected
usbcore: registered new interface driver usb-storage
i2c /dev entries driver
mpc-i2c ffe118100.i2c: timeout 1000000 us
mpc-i2c ffe119100.i2c: timeout 1000000 us
mpc-i2c ffe118000.i2c: timeout 1000000 us
mpc-i2c ffe119000.i2c: timeout 1000000 us
sdhci: Secure Digital Host Controller Interface driver
sdhci: Copyright(c) Pierre Ossman
------------[ cut here ]------------
WARNING: at arch/powerpc/kernel/iommu.c:1115
Modules linked in:
CPU: 2 PID: 1 Comm: swapper/0 Tainted: G        W    3.11.0-rc1-10505-g8d33=
668-dirty #16
task: c0000001fe080000 ti: c0000001fe07c000 task.ti: c0000001fe07c000
NIP: c00000000001b538 LR: c00000000007ea24 CTR: c00000000001b610
REGS: c0000001fe07ef10 TRAP: 0700   Tainted: G        W     (3.11.0-rc1-105=
05-g8d33668-dirty)
MSR: 0000000080029000 <CE,EE,ME>  CR: 24ad2e82  XER: 00000000
SOFTE: 1

GPR00: 0000000000000001 c0000001fe07f190 c000000000995fb8 c00000002e3f9a00=
=20
GPR04: 0000000000000001 c00000002e1eb010 ffffffffffffffff 0000000000000000=
=20
GPR08: 0000000000000000 0000000000000001 0000000000000000 0000000000000000=
=20
GPR12: 0000000024ad2e88 c00000000fffa800 c000000000001b30 0000000000000000=
=20
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR20: 0000000000000000 ata1: No Device OR PHYRDY change,Hstatus =3D 0xa000=
0000
ata1: SATA link down (SStatus 0 SControl 300)
0000000000000000 0000000000000000 0000000000000000=20
GPR24: c0000000009c6288 c0000000008a9090 ata2: No Device OR PHYRDY change,H=
status =3D 0xa0000000
ata2: SATA link down (SStatus 0 SControl 300)
0000000000000000 0000000000000000=20
GPR28: fffffffffffffffb c00000002e1eb010 c0000000008fc020 0000000000000001=
=20
NIP [c00000000001b538] .iommu_add_device+0x38/0x110
LR [c00000000007ea24] .notifier_call_chain+0x94/0xf0
Call Trace:
[c0000001fe07f190] [c0000000004cbe78] .iommu_bus_notifier+0xe8/0x180 (unrel=
iable)
[c0000001fe07f220] [c00000000007ea24] .notifier_call_chain+0x94/0xf0
[c0000001fe07f2c0] [c00000000007ef48] .__blocking_notifier_call_chain+0x58/=
0x90
[c0000001fe07f370] [c000000000373f9c] .device_add+0x4fc/0x710
[c0000001fe07f450] [c0000000004c1ff8] .of_device_add+0x68/0x80
[c0000001fe07f4c0] [c0000000004c29d4] .of_platform_device_create_pdata+0xb4=
/0xe0
[c0000001fe07f560] [c0000000004a7b90] .caam_jr_probe+0xa0/0x3e0
[c0000001fe07f620] [c0000000004a65f4] .caam_probe+0x1c4/0x8d0
[c0000001fe07f710] [c000000000379780] .platform_drv_probe+0x30/0x50
[c0000001fe07f780] [c000000000377bf4] .driver_probe_device+0xe4/0x2c0
[c0000001fe07f820] [c000000000377edc] .__driver_attach+0x10c/0x110
[c0000001fe07f8b0] [c0000000003756ac] .bus_for_each_dev+0xac/0x100
[c0000001fe07f950] [c000000000377648] .driver_attach+0x28/0x40
[c0000001fe07f9d0] [c000000000376fd8] .bus_add_driver+0x258/0x2d0
[c0000001fe07fa70] [c000000000378450] .driver_register+0xa0/0x1e0
[c0000001fe07fb10] [c000000000379be4] .__platform_driver_register+0x64/0x80
[c0000001fe07fb90] [c000000000874ca4] .caam_driver_init+0x20/0x38
[c0000001fe07fc10] [c00000000000138c] .do_one_initcall+0x5c/0x1b0
[c0000001fe07fd00] [c000000000851b28] .kernel_init_freeable+0x14c/0x214
[c0000001fe07fdb0] [c000000000001b4c] .kernel_init+0x1c/0x4d0
[c0000001fe07fe30] [c000000000000878] .ret_from_kernel_thread+0x5c/0x64
Instruction dump:
fba1ffe8 fbc1fff0 fbe1fff8 7c7d1b78 ebc28200 f8010010 f821ff71 e8630178=20
7c600074 7800d182 68000001 7c0907b4 <0b000000> 2fa90000 409e0068 eb9d0100=20
---[ end trace 1962e9b021548571 ]---
iommu_tce: device ffe301000.jr is already in iommu group 8, skipping
------------[ cut here ]------------
WARNING: at arch/powerpc/kernel/iommu.c:1115
Modules linked in:
CPU: 2 PID: 1 Comm: swapper/0 Tainted: G        W    3.11.0-rc1-10505-g8d33=
668-dirty #16
task: c0000001fe080000 ti: c0000001fe07c000 task.ti: c0000001fe07c000
NIP: c00000000001b538 LR: c00000000007ea24 CTR: c00000000001b610
REGS: c0000001fe07ef10 TRAP: 0700   Tainted: G        W     (3.11.0-rc1-105=
05-g8d33668-dirty)
MSR: 0000000080029000 <CE,EE,ME>  CR: 24ad2e82  XER: 00000000
SOFTE: 1

GPR00: 0000000000000001 c0000001fe07f190 c000000000995fb8 c00000002e3f9e00=
=20
GPR04: 0000000000000001 c00000002e1eb210 ffffffffffffffff 0000000000000000=
=20
GPR08: 0000000000000000 0000000000000001 0000000000000000 0000000000000000=
=20
GPR12: 0000000024ad2e88 c00000000fffa800 c000000000001b30 0000000000000000=
=20
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR24: c0000000009c6288 c0000000008a9090 0000000000000000 0000000000000000=
=20
GPR28: fffffffffffffffb c00000002e1eb210 c0000000008fc020 0000000000000001=
=20
NIP [c00000000001b538] .iommu_add_device+0x38/0x110
LR [c00000000007ea24] .notifier_call_chain+0x94/0xf0
Call Trace:
[c0000001fe07f190] [c0000000004cbe78] .iommu_bus_notifier+0xe8/0x180 (unrel=
iable)
[c0000001fe07f220] [c00000000007ea24] .notifier_call_chain+0x94/0xf0
[c0000001fe07f2c0] [c00000000007ef48] .__blocking_notifier_call_chain+0x58/=
0x90
[c0000001fe07f370] [c000000000373f9c] .device_add+0x4fc/0x710
[c0000001fe07f450] [c0000000004c1ff8] .of_device_add+0x68/0x80
[c0000001fe07f4c0] [c0000000004c29d4] .of_platform_device_create_pdata+0xb4=
/0xe0
[c0000001fe07f560] [c0000000004a7b90] .caam_jr_probe+0xa0/0x3e0
[c0000001fe07f620] [c0000000004a65f4] .caam_probe+0x1c4/0x8d0
[c0000001fe07f710] [c000000000379780] .platform_drv_probe+0x30/0x50
[c0000001fe07f780] [c000000000377bf4] .driver_probe_device+0xe4/0x2c0
[c0000001fe07f820] [c000000000377edc] .__driver_attach+0x10c/0x110
[c0000001fe07f8b0] [c0000000003756ac] .bus_for_each_dev+0xac/0x100
[c0000001fe07f950] [c000000000377648] .driver_attach+0x28/0x40
[c0000001fe07f9d0] [c000000000376fd8] .bus_add_driver+0x258/0x2d0
[c0000001fe07fa70] [c000000000378450] .driver_register+0xa0/0x1e0
[c0000001fe07fb10] [c000000000379be4] .__platform_driver_register+0x64/0x80
[c0000001fe07fb90] [c000000000874ca4] .caam_driver_init+0x20/0x38
[c0000001fe07fc10] [c00000000000138c] .do_one_initcall+0x5c/0x1b0
[c0000001fe07fd00] [c000000000851b28] .kernel_init_freeable+0x14c/0x214
[c0000001fe07fdb0] [c000000000001b4c] .kernel_init+0x1c/0x4d0
[c0000001fe07fe30] [c000000000000878] .ret_from_kernel_thread+0x5c/0x64
Instruction dump:
fba1ffe8 fbc1fff0 fbe1fff8 7c7d1b78 ebc28200 f8010010 f821ff71 e8630178=20
7c600074 7800d182 68000001 7c0907b4 <0b000000> 2fa90000 409e0068 eb9d0100=20
---[ end trace 1962e9b021548572 ]---
iommu_tce: device ffe302000.jr is already in iommu group 10, skipping
------------[ cut here ]------------
WARNING: at arch/powerpc/kernel/iommu.c:1115
Modules linked in:
CPU: 2 PID: 1 Comm: swapper/0 Tainted: G        W    3.11.0-rc1-10505-g8d33=
668-dirty #16
task: c0000001fe080000 ti: c0000001fe07c000 task.ti: c0000001fe07c000
NIP: c00000000001b538 LR: c00000000007ea24 CTR: c00000000001b610
REGS: c0000001fe07ef10 TRAP: 0700   Tainted: G        W     (3.11.0-rc1-105=
05-g8d33668-dirty)
MSR: 0000000080029000 <CE,EE,ME>  CR: 24ad2e82  XER: 00000000
SOFTE: 1

GPR00: 0000000000000001 c0000001fe07f190 c000000000995fb8 c0000001fe15f500=
=20
GPR04: 0000000000000001 c00000002e1eb410 ffffffffffffffff 0000000000000000=
=20
GPR08: 0000000000000000 0000000000000001 0000000000000000 0000000000000000=
=20
GPR12: 0000000024ad2e88 c00000000fffa800 c000000000001b30 0000000000000000=
=20
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR24: c0000000009c6288 c0000000008a9090 0000000000000000 0000000000000000=
=20
GPR28: fffffffffffffffb c00000002e1eb410 c0000000008fc020 0000000000000001=
=20
NIP [c00000000001b538] .iommu_add_device+0x38/0x110
LR [c00000000007ea24] .notifier_call_chain+0x94/0xf0
Call Trace:
[c0000001fe07f190] [c0000000004cbe78] .iommu_bus_notifier+0xe8/0x180 (unrel=
iable)
[c0000001fe07f220] [c00000000007ea24] .notifier_call_chain+0x94/0xf0
[c0000001fe07f2c0] [c00000000007ef48] .__blocking_notifier_call_chain+0x58/=
0x90
[c0000001fe07f370] [c000000000373f9c] .device_add+0x4fc/0x710
[c0000001fe07f450] [c0000000004c1ff8] .of_device_add+0x68/0x80
[c0000001fe07f4c0] [c0000000004c29d4] .of_platform_device_create_pdata+0xb4=
/0xe0
[c0000001fe07f560] [c0000000004a7b90] .caam_jr_probe+0xa0/0x3e0
[c0000001fe07f620] [c0000000004a65f4] .caam_probe+0x1c4/0x8d0
[c0000001fe07f710] [c000000000379780] .platform_drv_probe+0x30/0x50
[c0000001fe07f780] [c000000000377bf4] .driver_probe_device+0xe4/0x2c0
[c0000001fe07f820] [c000000000377edc] .__driver_attach+0x10c/0x110
[c0000001fe07f8b0] [c0000000003756ac] .bus_for_each_dev+0xac/0x100
[c0000001fe07f950] [c000000000377648] .driver_attach+0x28/0x40
[c0000001fe07f9d0] [c000000000376fd8] .bus_add_driver+0x258/0x2d0
[c0000001fe07fa70] [c000000000378450] .driver_register+0xa0/0x1e0
[c0000001fe07fb10] [c000000000379be4] .__platform_driver_register+0x64/0x80
[c0000001fe07fb90] [c000000000874ca4] .caam_driver_init+0x20/0x38
[c0000001fe07fc10] [c00000000000138c] .do_one_initcall+0x5c/0x1b0
[c0000001fe07fd00] [c000000000851b28] .kernel_init_freeable+0x14c/0x214
[c0000001fe07fdb0] [c000000000001b4c] .kernel_init+0x1c/0x4d0
[c0000001fe07fe30] [c000000000000878] .ret_from_kernel_thread+0x5c/0x64
Instruction dump:
fba1ffe8 fbc1fff0 fbe1fff8 7c7d1b78 ebc28200 f8010010 f821ff71 e8630178=20
7c600074 7800d182 68000001 7c0907b4 <0b000000> 2fa90000 409e0068 eb9d0100=20
---[ end trace 1962e9b021548573 ]---
iommu_tce: device ffe303000.jr is already in iommu group 11, skipping
------------[ cut here ]------------
WARNING: at arch/powerpc/kernel/iommu.c:1115
Modules linked in:
CPU: 1 PID: 1 Comm: swapper/0 Tainted: G        W    3.11.0-rc1-10505-g8d33=
668-dirty #16
task: c0000001fe080000 ti: c0000001fe07c000 task.ti: c0000001fe07c000
NIP: c00000000001b538 LR: c00000000007ea24 CTR: c00000000001b610
REGS: c0000001fe07ef10 TRAP: 0700   Tainted: G        W     (3.11.0-rc1-105=
05-g8d33668-dirty)
MSR: 0000000080029000 <CE,EE,ME>  CR: 24ad2e82  XER: 00000000
SOFTE: 1

GPR00: 0000000000000001 c0000001fe07f190 c000000000995fb8 c0000001fe13e800=
=20
GPR04: 0000000000000001 c0000001fe1df410 ffffffffffffffff 0000000000000000=
=20
GPR08: 0000000000000000 0000000000000001 0000000000000000 0000000000000000=
=20
GPR12: 0000000024ad2e88 c00000000fffa400 c000000000001b30 0000000000000000=
=20
GPR16: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR20: 0000000000000000 0000000000000000 0000000000000000 0000000000000000=
=20
GPR24: c0000000009c6288 c0000000008a9090 0000000000000000 0000000000000000=
=20
GPR28: fffffffffffffffb c0000001fe1df410 c0000000008fc020 0000000000000001=
=20
NIP [c00000000001b538] .iommu_add_device+0x38/0x110
LR [c00000000007ea24] .notifier_call_chain+0x94/0xf0
Call Trace:
[c0000001fe07f190] [c0000000004cbe78] .iommu_bus_notifier+0xe8/0x180 (unrel=
iable)
[c0000001fe07f220] [c00000000007ea24] .notifier_call_chain+0x94/0xf0
[c0000001fe07f2c0] [c00000000007ef48] .__blocking_notifier_call_chain+0x58/=
0x90
[c0000001fe07f370] [c000000000373f9c] .device_add+0x4fc/0x710
[c0000001fe07f450] [c0000000004c1ff8] .of_device_add+0x68/0x80
[c0000001fe07f4c0] [c0000000004c29d4] .of_platform_device_create_pdata+0xb4=
/0xe0
[c0000001fe07f560] [c0000000004a7b90] .caam_jr_probe+0xa0/0x3e0
[c0000001fe07f620] [c0000000004a65f4] .caam_probe+0x1c4/0x8d0
[c0000001fe07f710] [c000000000379780] .platform_drv_probe+0x30/0x50
[c0000001fe07f780] [c000000000377bf4] .driver_probe_device+0xe4/0x2c0
[c0000001fe07f820] [c000000000377edc] .__driver_attach+0x10c/0x110
[c0000001fe07f8b0] [c0000000003756ac] .bus_for_each_dev+0xac/0x100
[c0000001fe07f950] [c000000000377648] .driver_attach+0x28/0x40
[c0000001fe07f9d0] [c000000000376fd8] .bus_add_driver+0x258/0x2d0
[c0000001fe07fa70] [c000000000378450] .driver_register+0xa0/0x1e0
[c0000001fe07fb10] [c000000000379be4] .__platform_driver_register+0x64/0x80
[c0000001fe07fb90] [c000000000874ca4] .caam_driver_init+0x20/0x38
[c0000001fe07fc10] [c00000000000138c] .do_one_initcall+0x5c/0x1b0
[c0000001fe07fd00] [c000000000851b28] .kernel_init_freeable+0x14c/0x214
[c0000001fe07fdb0] [c000000000001b4c] .kernel_init+0x1c/0x4d0
[c0000001fe07fe30] [c000000000000878] .ret_from_kernel_thread+0x5c/0x64
Instruction dump:
fba1ffe8 fbc1fff0 fbe1fff8 7c7d1b78 ebc28200 f8010010 f821ff71 e8630178=20
7c600074 7800d182 68000001 7c0907b4 <0b000000> 2fa90000 409e0068 eb9d0100=20
---[ end trace 1962e9b021548574 ]---
iommu_tce: device ffe304000.jr is already in iommu group 12, skipping
caam ffe300000.crypto: device ID =3D 0x0a13010000000000 (Era -524)
caam ffe300000.crypto: job rings =3D 4, qi =3D 1
caam ffe300000.crypto: fsl,sec-v5.2 algorithms registered in /proc/crypto
platform ffe301000.jr: registering rng-caam
ipip: IPv4 over IPv4 tunneling driver
TCP: cubic registered
Initializing XFRM netlink socket
NET: Registered protocol family 10
sit: IPv6 over IPv4 tunneling driver
NET: Registered protocol family 17
NET: Registered protocol family 15
Key type dns_resolver registered
RAMDISK: gzip image found at block 0
VFS: Mounted root (ext2 filesystem) on device 1:0.
Freeing unused kernel memory: 268K (c000000000851000 - c000000000894000)
INIT: version 2.88 booting
Starting udev
Starting Bootlog daemon: bootlogd.
Configuring network interfaces... done.
net.ipv4.conf.default.rp_filter =3D 0
net.ipv4.conf.all.rp_filter =3D 0
hwclock: can't open '/dev/misc/rtc': No such file or directory
Wed Sep  5 19:45:00 UTC 2012
hwclock: can't open '/dev/misc/rtc': No such file or directory
Running postinst /etc/rpm-postinsts/100...
update-alternatives: Linking //usr/sbin/add-shell to add-shell.debianutils
update-alternatives: Linking //usr/sbin/installkernel to installkernel.debi=
anutils
update-alternatives: Linking //usr/sbin/mkboot to mkboot.debianutils
update-alternatives: Linking //usr/sbin/remove-shell to remove-shell.debian=
utils
update-alternatives: Linking //usr/bin/savelog to savelog.debianutils
update-alternatives: Linking //usr/bin/sensible-browser to sensible-browser=
.debianutils
update-alternatives: Linking //usr/bin/sensible-editor to sensible-editor.d=
ebianutils
update-alternatives: Linking //usr/bin/sensible-pager to sensible-pager.deb=
ianutils
update-alternatives: Linking //usr/bin/which to which.debianutils
update-alternatives: Linking //bin/run-parts to run-parts.debianutils
update-alternatives: Linking //bin/tempfile to tempfile.debianutils
Running postinst /etc/rpm-postinsts/101...
WARNING: -e needs -E or -F
WARNING: Couldn't open directory /lib/modules/3.11.0-rc1-10505-g8d33668-dir=
ty: No such file or directory
FATAL: Could not open /lib/modules/3.11.0-rc1-10505-g8d33668-dirty/modules.=
dep.temp for writing: No such file or directory
INIT: Entering runlevel: 5postinsts/102...
Starting Dropbear SSH server: Will output 1024 bit rsa secret key to '/etc/=
dropbear/dropbear_rsa_host_key'
Generating key, this may take a while...
Public key portion is:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgwClgV0llGONsTqePbqwQKb/s2n4fCDuFm8JWq1=
GCZ0sBFom2D0OXeQsS7fD6D8yhSDYjuk876kryMntPfxQEIXLXg/MHQe9YSXedxaF0RXXJHB1Fk=
hBSFJekqe5fLgLXZXTiaVqi74dhtq04ucBcDqu8LSI+A2NZJI5YUYiG4/rcndd root@p5040ds
Fingerprint: md5 61:74:c1:17:1b:88:92:3e:bc:3f:80:4d:22:24:32:ce
dropbear.
Starting network benchmark server: netserver.
Starting system log daemon...0
Starting kernel log daemon...0
Stopping Bootlog daemon: bootlogd.

Yocto (Built by Poky 7.0) 1.2 p5040ds ttyS0

p5040ds login:

---------------------- End of bootlog--------------------------------------

And this simple fix work for me=20
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index b20ff17..8869b0d 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -48,6 +48,8 @@
 #include <asm/vio.h>
 #include <asm/tce.h>
=20
+#define DEBUG
+
 #define DBG(...)
=20
 static int novmerge;
@@ -871,7 +873,7 @@ void iommu_free_coherent(struct iommu_table *tbl, size_=
t size,
        }
 }
=20
-#ifdef CONFIG_IOMMU_API
+#ifdef SPAPR_TCE_IOMMU
 /*
  * SPAPR TCE API
  */
----------------------


Thanks
-Bharat

^ permalink raw reply related

* Re: [PATCH v4 09/31] powerpc/fsl-pci: improve clock API use
From: Gerhard Sittig @ 2013-08-12  7:57 UTC (permalink / raw)
  To: Anatolij Gustschin; +Cc: linuxppc-dev
In-Reply-To: <20130808221231.6d3046c5@crub>

On Thu, Aug 08, 2013 at 22:12 +0200, Anatolij Gustschin wrote:
> 
> On Tue,  6 Aug 2013 22:43:49 +0200
> Gerhard Sittig <gsi@denx.de> wrote:
> ...
> > diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
> > index 46ac1dd..549ff08 100644
> > --- a/arch/powerpc/sysdev/fsl_pci.c
> > +++ b/arch/powerpc/sysdev/fsl_pci.c
> ...
> > +	clk = devm_clk_get(&pdev->dev, "per");
> > +	if (!IS_ERR(clk)) {
> > +		ret = clk_prepare_enable(clk);
> > +		if (ret) {
> > +			dev_err(dev, "Could not enable peripheral clock\n");
> 
> above line will break building. s/dev,/&pdev->dev,/

Thank you for testing and for the feedback.

I've queued this fix for v5.  The bug could hide because the file
gets compiled on MPC512x, but this specific routine sits behind
even more compile time switches (85xx and 86xx related).


virtually yours
Gerhard Sittig
-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr. 5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80  Email: office@denx.de

^ permalink raw reply

* Re: [PATCH v2 2/2] powerpc: Use ibm, chip-id property to compute cpu_core_mask if available
From: Vasant Hegde @ 2013-08-12  7:55 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev, Stephen Rothwell
In-Reply-To: <20130812062933.GC20246@iris.ozlabs.ibm.com>

On 08/12/2013 11:59 AM, Paul Mackerras wrote:
> Some systems have an ibm,chip-id property in the cpu nodes in the
> device tree.  On these systems, we now use that to compute the
> cpu_core_mask (i.e. the set of core siblings) rather than looking
> at cache properties.

Looks fine.

Tested-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>

-Vasant

>
> Signed-off-by: Paul Mackerras <paulus@samba.org>
> ---
> v2: add is bool, use of_read_number
>
>   arch/powerpc/kernel/smp.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---
>   1 file changed, 45 insertions(+), 3 deletions(-)
>
> diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
> index 1568525..a4137de 100644
> --- a/arch/powerpc/kernel/smp.c
> +++ b/arch/powerpc/kernel/smp.c
> @@ -587,6 +587,33 @@ int cpu_first_thread_of_core(int core)
>   }
>   EXPORT_SYMBOL_GPL(cpu_first_thread_of_core);
>
> +static void traverse_siblings_chip_id(int cpu, bool add, int chipid)
> +{
> +	const struct cpumask *mask;
> +	struct device_node *np;
> +	int i, plen;
> +	const __be32 *prop;
> +
> +	mask = add ? cpu_online_mask : cpu_present_mask;
> +	for_each_cpu(i, mask) {
> +		np = of_get_cpu_node(i, NULL);
> +		if (!np)
> +			continue;
> +		prop = of_get_property(np, "ibm,chip-id", &plen);
> +		if (prop && plen == sizeof(int) &&
> +		    of_read_number(prop, 1) == chipid) {
> +			if (add) {
> +				cpumask_set_cpu(cpu, cpu_core_mask(i));
> +				cpumask_set_cpu(i, cpu_core_mask(cpu));
> +			} else {
> +				cpumask_clear_cpu(cpu, cpu_core_mask(i));
> +				cpumask_clear_cpu(i, cpu_core_mask(cpu));
> +			}
> +		}
> +		of_node_put(np);
> +	}
> +}
> +
>   /* Must be called when no change can occur to cpu_present_mask,
>    * i.e. during cpu online or offline.
>    */
> @@ -611,14 +638,29 @@ static struct device_node *cpu_to_l2cache(int cpu)
>
>   static void traverse_core_siblings(int cpu, bool add)
>   {
> -	struct device_node *l2_cache;
> +	struct device_node *l2_cache, *np;
>   	const struct cpumask *mask;
> -	int i;
> +	int i, chip, plen;
> +	const __be32 *prop;
> +
> +	/* First see if we have ibm,chip-id properties in cpu nodes */
> +	np = of_get_cpu_node(cpu, NULL);
> +	if (np) {
> +		chip = -1;
> +		prop = of_get_property(np, "ibm,chip-id", &plen);
> +		if (prop && plen == sizeof(int))
> +			chip = of_read_number(prop, 1);
> +		of_node_put(np);
> +		if (chip >= 0) {
> +			traverse_siblings_chip_id(cpu, add, chip);
> +			return;
> +		}
> +	}
>
>   	l2_cache = cpu_to_l2cache(cpu);
>   	mask = add ? cpu_online_mask : cpu_present_mask;
>   	for_each_cpu(i, mask) {
> -		struct device_node *np = cpu_to_l2cache(i);
> +		np = cpu_to_l2cache(i);
>   		if (!np)
>   			continue;
>   		if (np == l2_cache) {
>

^ permalink raw reply

* [PATCH v2 2/2] powerpc: Use ibm,chip-id property to compute cpu_core_mask if available
From: Paul Mackerras @ 2013-08-12  6:29 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Vasant Hegde, Stephen Rothwell
In-Reply-To: <20130812062847.GB20246@iris.ozlabs.ibm.com>

Some systems have an ibm,chip-id property in the cpu nodes in the
device tree.  On these systems, we now use that to compute the
cpu_core_mask (i.e. the set of core siblings) rather than looking
at cache properties.

Signed-off-by: Paul Mackerras <paulus@samba.org>
---
v2: add is bool, use of_read_number

 arch/powerpc/kernel/smp.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 45 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index 1568525..a4137de 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -587,6 +587,33 @@ int cpu_first_thread_of_core(int core)
 }
 EXPORT_SYMBOL_GPL(cpu_first_thread_of_core);
 
+static void traverse_siblings_chip_id(int cpu, bool add, int chipid)
+{
+	const struct cpumask *mask;
+	struct device_node *np;
+	int i, plen;
+	const __be32 *prop;
+
+	mask = add ? cpu_online_mask : cpu_present_mask;
+	for_each_cpu(i, mask) {
+		np = of_get_cpu_node(i, NULL);
+		if (!np)
+			continue;
+		prop = of_get_property(np, "ibm,chip-id", &plen);
+		if (prop && plen == sizeof(int) &&
+		    of_read_number(prop, 1) == chipid) {
+			if (add) {
+				cpumask_set_cpu(cpu, cpu_core_mask(i));
+				cpumask_set_cpu(i, cpu_core_mask(cpu));
+			} else {
+				cpumask_clear_cpu(cpu, cpu_core_mask(i));
+				cpumask_clear_cpu(i, cpu_core_mask(cpu));
+			}
+		}
+		of_node_put(np);
+	}
+}
+
 /* Must be called when no change can occur to cpu_present_mask,
  * i.e. during cpu online or offline.
  */
@@ -611,14 +638,29 @@ static struct device_node *cpu_to_l2cache(int cpu)
 
 static void traverse_core_siblings(int cpu, bool add)
 {
-	struct device_node *l2_cache;
+	struct device_node *l2_cache, *np;
 	const struct cpumask *mask;
-	int i;
+	int i, chip, plen;
+	const __be32 *prop;
+
+	/* First see if we have ibm,chip-id properties in cpu nodes */
+	np = of_get_cpu_node(cpu, NULL);
+	if (np) {
+		chip = -1;
+		prop = of_get_property(np, "ibm,chip-id", &plen);
+		if (prop && plen == sizeof(int))
+			chip = of_read_number(prop, 1);
+		of_node_put(np);
+		if (chip >= 0) {
+			traverse_siblings_chip_id(cpu, add, chip);
+			return;
+		}
+	}
 
 	l2_cache = cpu_to_l2cache(cpu);
 	mask = add ? cpu_online_mask : cpu_present_mask;
 	for_each_cpu(i, mask) {
-		struct device_node *np = cpu_to_l2cache(i);
+		np = cpu_to_l2cache(i);
 		if (!np)
 			continue;
 		if (np == l2_cache) {
-- 
1.8.4.rc2

^ permalink raw reply related

* [PATCH v2 1/2] powerpc: Pull out cpu_core_mask updates into a separate function
From: Paul Mackerras @ 2013-08-12  6:28 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Vasant Hegde, Stephen Rothwell

This factors out the details of updating cpu_core_mask into a separate
function, to make it easier to change how the mask is calculated later.
This makes no functional change.

Signed-off-by: Paul Mackerras <paulus@samba.org>
---
v2: add is bool

 arch/powerpc/kernel/smp.c | 56 +++++++++++++++++++++++------------------------
 1 file changed, 28 insertions(+), 28 deletions(-)

diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index 38b0ba6..1568525 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -609,11 +609,36 @@ static struct device_node *cpu_to_l2cache(int cpu)
 	return cache;
 }
 
+static void traverse_core_siblings(int cpu, bool add)
+{
+	struct device_node *l2_cache;
+	const struct cpumask *mask;
+	int i;
+
+	l2_cache = cpu_to_l2cache(cpu);
+	mask = add ? cpu_online_mask : cpu_present_mask;
+	for_each_cpu(i, mask) {
+		struct device_node *np = cpu_to_l2cache(i);
+		if (!np)
+			continue;
+		if (np == l2_cache) {
+			if (add) {
+				cpumask_set_cpu(cpu, cpu_core_mask(i));
+				cpumask_set_cpu(i, cpu_core_mask(cpu));
+			} else {
+				cpumask_clear_cpu(cpu, cpu_core_mask(i));
+				cpumask_clear_cpu(i, cpu_core_mask(cpu));
+			}
+		}
+		of_node_put(np);
+	}
+	of_node_put(l2_cache);
+}
+
 /* Activate a secondary processor. */
 void start_secondary(void *unused)
 {
 	unsigned int cpu = smp_processor_id();
-	struct device_node *l2_cache;
 	int i, base;
 
 	atomic_inc(&init_mm.mm_count);
@@ -652,18 +677,7 @@ void start_secondary(void *unused)
 		cpumask_set_cpu(cpu, cpu_core_mask(base + i));
 		cpumask_set_cpu(base + i, cpu_core_mask(cpu));
 	}
-	l2_cache = cpu_to_l2cache(cpu);
-	for_each_online_cpu(i) {
-		struct device_node *np = cpu_to_l2cache(i);
-		if (!np)
-			continue;
-		if (np == l2_cache) {
-			cpumask_set_cpu(cpu, cpu_core_mask(i));
-			cpumask_set_cpu(i, cpu_core_mask(cpu));
-		}
-		of_node_put(np);
-	}
-	of_node_put(l2_cache);
+	traverse_core_siblings(cpu, true);
 
 	smp_wmb();
 	notify_cpu_starting(cpu);
@@ -719,7 +733,6 @@ int arch_sd_sibling_asym_packing(void)
 #ifdef CONFIG_HOTPLUG_CPU
 int __cpu_disable(void)
 {
-	struct device_node *l2_cache;
 	int cpu = smp_processor_id();
 	int base, i;
 	int err;
@@ -739,20 +752,7 @@ int __cpu_disable(void)
 		cpumask_clear_cpu(cpu, cpu_core_mask(base + i));
 		cpumask_clear_cpu(base + i, cpu_core_mask(cpu));
 	}
-
-	l2_cache = cpu_to_l2cache(cpu);
-	for_each_present_cpu(i) {
-		struct device_node *np = cpu_to_l2cache(i);
-		if (!np)
-			continue;
-		if (np == l2_cache) {
-			cpumask_clear_cpu(cpu, cpu_core_mask(i));
-			cpumask_clear_cpu(i, cpu_core_mask(cpu));
-		}
-		of_node_put(np);
-	}
-	of_node_put(l2_cache);
-
+	traverse_core_siblings(cpu, false);
 
 	return 0;
 }
-- 
1.8.4.rc2

^ permalink raw reply related

* [PATCH v4] powerpc/eeh: powerpc/eeh: Fix undefined variable
From: Mike Qiu @ 2013-08-12  6:15 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Mike Qiu, shangw

changes for V4:
	- changes the type of frozen_pe_no from %d to %llu
	  in pr_devel()

'pe_no' hasn't been defined, it should be an typo error,
it should be 'frozen_pe_no'.

Also '__func__' has missed in IODA_EEH_DBG(),

For safety reasons, use pr_devel() directly, instead
of use IODA_EEH_DBG()

Signed-off-by: Mike Qiu <qiudayu@linux.vnet.ibm.com>
---
 arch/powerpc/platforms/powernv/eeh-ioda.c | 22 ++++++++--------------
 1 file changed, 8 insertions(+), 14 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c
index 0cd1c4a..cf42e74 100644
--- a/arch/powerpc/platforms/powernv/eeh-ioda.c
+++ b/arch/powerpc/platforms/powernv/eeh-ioda.c
@@ -36,13 +36,6 @@
 #include "powernv.h"
 #include "pci.h"
 
-/* Debugging option */
-#ifdef IODA_EEH_DBG_ON
-#define IODA_EEH_DBG(args...)	pr_info(args)
-#else
-#define IODA_EEH_DBG(args...)
-#endif
-
 static char *hub_diag = NULL;
 static int ioda_eeh_nb_init = 0;
 
@@ -823,17 +816,17 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
 
 		/* If OPAL API returns error, we needn't proceed */
 		if (rc != OPAL_SUCCESS) {
-			IODA_EEH_DBG("%s: Invalid return value on "
-				     "PHB#%x (0x%lx) from opal_pci_next_error",
-				     __func__, hose->global_number, rc);
+			pr_devel("%s: Invalid return value on "
+				 "PHB#%x (0x%lx) from opal_pci_next_error",
+				 __func__, hose->global_number, rc);
 			continue;
 		}
 
 		/* If the PHB doesn't have error, stop processing */
 		if (err_type == OPAL_EEH_NO_ERROR ||
 		    severity == OPAL_EEH_SEV_NO_ERROR) {
-			IODA_EEH_DBG("%s: No error found on PHB#%x\n",
-				     __func__, hose->global_number);
+			pr_devel("%s: No error found on PHB#%x\n",
+				 __func__, hose->global_number);
 			continue;
 		}
 
@@ -842,8 +835,9 @@ static int ioda_eeh_next_error(struct eeh_pe **pe)
 		 * highest priority reported upon multiple errors on the
 		 * specific PHB.
 		 */
-		IODA_EEH_DBG("%s: Error (%d, %d, %d) on PHB#%x\n",
-			err_type, severity, pe_no, hose->global_number);
+		pr_devel("%s: Error (%d, %d, %llu) on PHB#%x\n",
+			 __func__, err_type, severity,
+			 frozen_pe_no, hose->global_number);
 		switch (err_type) {
 		case OPAL_EEH_IOC_ERROR:
 			if (severity == OPAL_EEH_SEV_IOC_DEAD) {
-- 
1.8.2.1

^ permalink raw reply related

* [PATCH] powerpc: Fix denormalized exception handler
From: Paul Mackerras @ 2013-08-12  6:12 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Anton Blanchard

The denormalized exception handler (denorm_exception_hv) has a couple
of bugs.  If the CONFIG_PPC_DENORMALISATION option is not selected,
or the HSRR1_DENORM bit is not set in HSRR1, we don't test whether the
interrupt occurred within a KVM guest.  On the other hand, if the
HSRR1_DENORM bit is set and CONFIG_PPC_DENORMALISATION is enabled,
we corrupt the CFAR and PPR.

To correct these problems, this replaces the open-coded version of
EXCEPTION_PROLOG_1 that is there currently, and that is missing the
saving of PPR and CFAR values to the PACA, with an instance of
EXCEPTION_PROLOG_1.  This adds an explicit KVMTEST after testing
whether the exception is one we can handle, and adds code to restore
the CFAR on exit.

Signed-off-by: Paul Mackerras <paulus@samba.org>
---
 arch/powerpc/kernel/exceptions-64s.S | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index e3c8a03..3e06b26 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -360,11 +360,7 @@ denorm_exception_hv:
 	HMT_MEDIUM_PPR_DISCARD
 	mtspr	SPRN_SPRG_HSCRATCH0,r13
 	EXCEPTION_PROLOG_0(PACA_EXGEN)
-	std	r11,PACA_EXGEN+EX_R11(r13)
-	std	r12,PACA_EXGEN+EX_R12(r13)
-	mfspr	r9,SPRN_SPRG_HSCRATCH0
-	std	r9,PACA_EXGEN+EX_R13(r13)
-	mfcr	r9
+	EXCEPTION_PROLOG_1(PACA_EXGEN, NOTEST, 0x1500)
 
 #ifdef CONFIG_PPC_DENORMALISATION
 	mfspr	r10,SPRN_HSRR1
@@ -374,6 +370,7 @@ denorm_exception_hv:
 	bne+	denorm_assist
 #endif
 
+	KVMTEST(0x1500)
 	EXCEPTION_PROLOG_PSERIES_1(denorm_common, EXC_HV)
 	KVM_HANDLER_SKIP(PACA_EXGEN, EXC_STD, 0x1500)
 
@@ -494,6 +491,10 @@ denorm_done:
 	mtcrf	0x80,r9
 	ld	r9,PACA_EXGEN+EX_R9(r13)
 	RESTORE_PPR_PACA(PACA_EXGEN, r10)
+BEGIN_FTR_SECTION
+	ld	r10,PACA_EXGEN+EX_CFAR(r13)
+	mtspr	SPRN_CFAR,r10
+END_FTR_SECTION_IFSET(CPU_FTR_CFAR)
 	ld	r10,PACA_EXGEN+EX_R10(r13)
 	ld	r11,PACA_EXGEN+EX_R11(r13)
 	ld	r12,PACA_EXGEN+EX_R12(r13)
-- 
1.8.4.rc2

^ permalink raw reply related

* Re: Build errors on mainline kernel
From: Sukadev Bhattiprolu @ 2013-08-12  6:02 UTC (permalink / raw)
  To: Michael Ellerman; +Cc: linuxppc-dev
In-Reply-To: <1376286123.2066.7.camel@concordia>

Michael Ellerman [michael@ellerman.id.au] wrote:
| On Fri, 2013-08-09 at 11:24 -0700, Sukadev Bhattiprolu wrote:
| > I am tryng to compile clean mainline kernel with a few different config files
| > and running into errors with some configs.
| > 
| > I am building on RHEL6.3 with following binaries:
| > 
| > 	gcc (GCC) 4.4.6 20120305 (Red Hat 4.4.6-4)
| > 	GNU ld version 2.20.51.0.2-5.34.el6 20100205
| > 	binutils-2.20.51.0.2-5.34.el6.ppc64
| > 	binutils-devel-2.20.51.0.2-5.34.el6.ppc64
| > 
| > I am getting the error with several files and configs, but other configs 
| > (eg: ppc64_defconfig, pmac32_defconfig) build fine.
| > 
| > For instance, with latest mainline kernel (commit 6c2580c) and ppc64_defconfig, I get:
| 
| Here you say ppc64_defconfig ..

That was a stupid typo. ppc64_defconfig works.

| 
| > 	make O=linux-obj mrproper
| > 	make O=linux-obj ppc64e_defconfig
| 
| But here you say ppc64e_defconfig ?

ppc64e_defconfig fails (checked again now).

^ permalink raw reply

* Re: Build errors on mainline kernel
From: Michael Ellerman @ 2013-08-12  5:42 UTC (permalink / raw)
  To: Sukadev Bhattiprolu; +Cc: linuxppc-dev
In-Reply-To: <20130809182446.GA26711@us.ibm.com>

On Fri, 2013-08-09 at 11:24 -0700, Sukadev Bhattiprolu wrote:
> I am tryng to compile clean mainline kernel with a few different config files
> and running into errors with some configs.
> 
> I am building on RHEL6.3 with following binaries:
> 
> 	gcc (GCC) 4.4.6 20120305 (Red Hat 4.4.6-4)
> 	GNU ld version 2.20.51.0.2-5.34.el6 20100205
> 	binutils-2.20.51.0.2-5.34.el6.ppc64
> 	binutils-devel-2.20.51.0.2-5.34.el6.ppc64
> 
> I am getting the error with several files and configs, but other configs 
> (eg: ppc64_defconfig, pmac32_defconfig) build fine.
> 
> For instance, with latest mainline kernel (commit 6c2580c) and ppc64_defconfig, I get:

Here you say ppc64_defconfig ..

> 	make O=linux-obj mrproper
> 	make O=linux-obj ppc64e_defconfig

But here you say ppc64e_defconfig ?

> 	make O=linux-obj arch/powerpc/platforms/85xx/smp.o
> 	  ...
> 
> 	CC      arch/powerpc/platforms/85xx/smp.o
> 	{standard input}: Assembler messages:
> 	{standard input}:240: Error: junk at end of line: `1'
> 	make[2]: *** [arch/powerpc/platforms/85xx/smp.o] Error 1
> 	make[1]: *** [arch/powerpc/platforms/85xx/smp.o] Error 2

So I'm not clear which config you're building.

cheers

^ permalink raw reply

* Re: [PATCH 2/2] powerpc: Use ibm,chip-id property to compute cpu_core_mask if available
From: Stephen Rothwell @ 2013-08-12  5:02 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: Vasant Hegde, linuxppc-dev
In-Reply-To: <20130810034615.GE5240@iris.ozlabs.ibm.com>

[-- Attachment #1: Type: text/plain, Size: 1274 bytes --]

Hi Paul,

On Sat, 10 Aug 2013 13:46:15 +1000 Paul Mackerras <paulus@samba.org> wrote:
>
> +static void traverse_siblings_chip_id(int cpu, int add, int chipid)

Again, the "add is a boolean.

> +{
> +	const struct cpumask *mask;
> +	struct device_node *np;
> +	int i, plen;
> +	const int *prop;
> +
> +	mask = add ? cpu_online_mask : cpu_present_mask;
> +	for_each_cpu(i, mask) {
> +		np = of_get_cpu_node(i, NULL);
> +		if (!np)
> +			continue;
> +		prop = of_get_property(np, "ibm,chip-id", &plen);
> +		if (prop && plen == sizeof(int) && *prop == chipid) {
                                                   ^^^^^
You should be using of_read_number(), I think.

>  static void traverse_core_siblings(int cpu, int add)
>  {
> -	struct device_node *l2_cache;
> +	struct device_node *l2_cache, *np;
>  	const struct cpumask *mask;
> -	int i;
> +	int i, chip, plen;
> +	const int *prop;
> +
> +	/* First see if we have ibm,chip-id properties in cpu nodes */
> +	np = of_get_cpu_node(cpu, NULL);
> +	if (np) {
> +		chip = -1;
> +		prop = of_get_property(np, "ibm,chip-id", &plen);
> +		if (prop && plen == sizeof(int))
> +			chip = *(int *)prop;

Here as well.

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 1/2] powerpc: Pull out cpu_core_mask updates into a separate function
From: Stephen Rothwell @ 2013-08-12  4:41 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: Vasant Hegde, linuxppc-dev
In-Reply-To: <20130810034530.GD5240@iris.ozlabs.ibm.com>

[-- Attachment #1: Type: text/plain, Size: 975 bytes --]

Hi Paul,

On Sat, 10 Aug 2013 13:45:30 +1000 Paul Mackerras <paulus@samba.org> wrote:
>
> This factors out the details of updating cpu_core_mask into a separate
> function, to make it easier to change how the mask is calculated later.
> This makes no functional change.
> 
> Signed-off-by: Paul Mackerras <paulus@samba.org>
> ---
>  arch/powerpc/kernel/smp.c | 56 +++++++++++++++++++++++------------------------
>  1 file changed, 28 insertions(+), 28 deletions(-)
> 
> diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
> index 38b0ba6..663cefd 100644
> --- a/arch/powerpc/kernel/smp.c
> +++ b/arch/powerpc/kernel/smp.c
> @@ -609,11 +609,36 @@ static struct device_node *cpu_to_l2cache(int cpu)
>  	return cache;
>  }
>  
> +static void traverse_core_siblings(int cpu, int add)

This "add" parameter is only used as a boolean, so should be declared
that way.

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 5/7] powerpc/perf: Define big-endian version of perf_mem_data_src
From: Vince Weaver @ 2013-08-12  3:19 UTC (permalink / raw)
  To: Michael Ellerman
  Cc: Anton Blanchard, linux-kernel, Stephane Eranian, linuxppc-dev,
	Paul Mackerras, Sukadev Bhattiprolu, Anshuman Khandual
In-Reply-To: <20130811235758.GA29824@concordia>

On Mon, 12 Aug 2013, Michael Ellerman wrote:
> 
> Yes I think so. The interface is already defined and it's little endian,
> so on big endian we just need to swap.
> 
> The only part I'm not clear on is how things are handled in perf
> userspace, it seems to already do some byte swapping.

It would be nice to clarify this.

"struct perf_branch_entry" also has bitfields like this, though to make
things more confusing that structure isn't exported via the uapi header
so it's not clear how userspace code is supposed to interpret the values.

As you say it gets complicated with perf userspace, especially in cases
where you record the data on big-endian but then try to analyze the
results on a little-endian machine.

It would be nice to get confirmation that these bitfields will always be 
little-endian.  I guess they currently are by definition because only 
x86/pebs sets data.data_src.val so far?

Vince

^ permalink raw reply

* [PATCH 3/3 V3] mmc:esdhc: add support to get voltage from device-tree
From: Haijun Zhang @ 2013-08-12  1:39 UTC (permalink / raw)
  To: linux-mmc, linuxppc-dev; +Cc: X.Xie, cbouatmailru, scottwood, cjb, Haijun Zhang
In-Reply-To: <1376271546-25085-1-git-send-email-Haijun.Zhang@freescale.com>

Add suppport to get voltage from device-tree node for esdhc host,
if voltage-ranges was specified in device-tree node we can get
ocr_mask instead of read from host capacity register. If not voltages
still can be get from host capacity register.

Signed-off-by: Haijun Zhang <haijun.zhang@freescale.com>
---
changes for V3:
	- changed the parameter of function

 drivers/mmc/host/sdhci-of-esdhc.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 15039e2..e328252 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -316,6 +316,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
 
 	/* call to generic mmc_of_parse to support additional capabilities */
 	mmc_of_parse(host->mmc);
+	mmc_of_parse_voltage(np, &host->ocr_mask);
 
 	ret = sdhci_add_host(host);
 	if (ret)
-- 
1.8.0

^ permalink raw reply related

* Re: [PATCH 1/3 V2] mmc:core: parse voltage from device-tree
From: Zhang Haijun @ 2013-08-12  2:46 UTC (permalink / raw)
  To: Kumar Gala
  Cc: linux-mmc, cbouatmailru, scottwood, cjb, linuxppc-dev,
	Haijun Zhang
In-Reply-To: <733059D3-105A-437B-B330-763D2C1859B2@kernel.crashing.org>

[-- Attachment #1: Type: text/plain, Size: 770 bytes --]

On 08/09/2013 10:48 PM, Kumar Gala wrote:
> On Jul 31, 2013, at 1:25 AM, Haijun Zhang wrote:
>
>> Add function to support get voltage from device-tree.
>> If there are voltage-range specified in device-tree node, this function
>> will parse it and return the avail voltage mask.
>>
>> Signed-off-by: Haijun Zhang <haijun.zhang@freescale.com>
>> ---
>> changes for v2:
>> 	- Update the parameters of function
>>
>> drivers/mmc/core/core.c  | 46 ++++++++++++++++++++++++++++++++++++++++++++++
>> include/linux/mmc/core.h |  1 +
>> 2 files changed, 47 insertions(+)
> There should be a device tree binding spec update to go with this patch series.
>
> - k
Hi, kumar

I'll update this tree binding spec after the patch set is accept by Anton.

-- 
Thanks & Regards

Haijun


[-- Attachment #2: Type: text/html, Size: 1578 bytes --]

^ permalink raw reply

* [PATCH 2/3 V3] mmc:sdhc: get voltage from sdhc host
From: Haijun Zhang @ 2013-08-12  1:39 UTC (permalink / raw)
  To: linux-mmc, linuxppc-dev; +Cc: X.Xie, cbouatmailru, scottwood, cjb, Haijun Zhang
In-Reply-To: <1376271546-25085-1-git-send-email-Haijun.Zhang@freescale.com>

We use host->ocr_mask to hold the voltage get from device-tree
node, In case host->ocr_mask was available, we use host->ocr_mask
as the final available voltage can be used by MMC/SD/SDIO card.

Signed-off-by: Haijun Zhang <haijun.zhang@freescale.com>
---
changes for V3:
	- changed the type of mask

 drivers/mmc/host/sdhci.c  | 3 +++
 include/linux/mmc/sdhci.h | 1 +
 2 files changed, 4 insertions(+)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index a78bd4f..57541e0 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -3119,6 +3119,9 @@ int sdhci_add_host(struct sdhci_host *host)
 				   SDHCI_MAX_CURRENT_MULTIPLIER;
 	}
 
+	if (host->ocr_mask)
+		ocr_avail = host->ocr_mask;
+
 	mmc->ocr_avail = ocr_avail;
 	mmc->ocr_avail_sdio = ocr_avail;
 	if (host->ocr_avail_sdio)
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index e3c6a74..3e781b8 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -171,6 +171,7 @@ struct sdhci_host {
 	unsigned int            ocr_avail_sdio;	/* OCR bit masks */
 	unsigned int            ocr_avail_sd;
 	unsigned int            ocr_avail_mmc;
+	u32 ocr_mask;		/* available voltages */
 
 	wait_queue_head_t	buf_ready_int;	/* Waitqueue for Buffer Read Ready interrupt */
 	unsigned int		tuning_done;	/* Condition flag set when CMD19 succeeds */
-- 
1.8.0

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox