LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [RFC PATCH 3/4] powerpc: refactor of_get_cpu_node to support other architectures
From: Benjamin Herrenschmidt @ 2013-08-16 12:32 UTC (permalink / raw)
  To: Sudeep KarkadaNagesha
  Cc: Jonas Bonn, devicetree@vger.kernel.org, Michal Simek,
	linux-pm@vger.kernel.org, microblaze-uclinux@itee.uq.edu.au,
	linux@openrisc.net, linux-kernel@vger.kernel.org,
	rob.herring@calxeda.com, Rafael J. Wysocki,
	grant.likely@linaro.org, linuxppc-dev@lists.ozlabs.org,
	linux-arm-kernel@lists.infradead.org
In-Reply-To: <520DE753.8090601@arm.com>

On Fri, 2013-08-16 at 09:48 +0100, Sudeep KarkadaNagesha wrote:

> > Naming is a bit gross. You might want to make it clearer that
> > we are talking about CPU IDs in the device-tree here.
> > 
> Any particular preference to the name or just a note is sufficient.
> Also unlike PPC, in ARM we don't set hard processor id value based
> values read from device tree. DT must contain the values matching to the
> hardware ID registers.

This is exactly the same on ppc. We don't "set" HW values. The
device-tree content matches the HW internals. Some processors have a
"PIR" register as well which contains the HW value, in this case the
device-tree must contain the same value as the PIR on that processor.

> >> +static bool __of_find_n_match_cpu_property(struct device_node *cpun,
> >> +			const char *prop_name, int cpu, unsigned int *thread)
> >> +{
> >> +	const __be32 *cell;
> >> +	int ac, prop_len, tid;
> >> +	u64 hwid;
> >> +
> >> +	ac = of_n_addr_cells(cpun);
> >> +	cell = of_get_property(cpun, prop_name, &prop_len);
> >> +	if (!cell)
> >> +		return false;
> >> +	prop_len /= sizeof(*cell);
> >> +	for (tid = 0; tid < prop_len; tid++) {
> >> +		hwid = of_read_number(cell, ac);
> >> +		if (arch_match_cpu_phys_id(cpu, hwid)) {
> >> +			if (thread)
> >> +				*thread = tid;
> >> +			return true;
> >> +		}
> > 
> > Missing:          cell += ac;
> Ah, missed it while refactoring, will fix it. Thanks

Ben.

> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply

* Re: [RFC PATCH 3/4] powerpc: refactor of_get_cpu_node to support other architectures
From: Sudeep KarkadaNagesha @ 2013-08-16 12:44 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Jonas Bonn, devicetree@vger.kernel.org, Michal Simek,
	linux-pm@vger.kernel.org, Sudeep KarkadaNagesha,
	microblaze-uclinux@itee.uq.edu.au, linux@openrisc.net,
	linux-kernel@vger.kernel.org, rob.herring@calxeda.com,
	Rafael J. Wysocki, grant.likely@linaro.org,
	linuxppc-dev@lists.ozlabs.org,
	linux-arm-kernel@lists.infradead.org
In-Reply-To: <1376656351.25016.2.camel@pasglop>

On 16/08/13 13:32, Benjamin Herrenschmidt wrote:
> On Fri, 2013-08-16 at 09:48 +0100, Sudeep KarkadaNagesha wrote:
>=20
>>> Naming is a bit gross. You might want to make it clearer that
>>> we are talking about CPU IDs in the device-tree here.
>>>
>> Any particular preference to the name or just a note is sufficient.
>> Also unlike PPC, in ARM we don't set hard processor id value based
>> values read from device tree. DT must contain the values matching to the
>> hardware ID registers.
>=20
> This is exactly the same on ppc. We don't "set" HW values. The
> device-tree content matches the HW internals. Some processors have a
> "PIR" register as well which contains the HW value, in this case the
> device-tree must contain the same value as the PIR on that processor.
>=20
Ok, I misread the function 'set_hard_smp_processor_id' function.
BTW, you didn't mention if you are OK by just have this clearly
documented in the function and/or you have any preference/better name.

I will send the next version based on that. I have even compile tested
:) now on PPC.

Regards,
Sudeep

^ permalink raw reply

* [PATCH v6 0/2] Add freescale S/PDIF CPU DAI and machine drivers
From: Nicolin Chen @ 2013-08-16 12:56 UTC (permalink / raw)
  To: broonie, lars, p.zabel, s.hauer
  Cc: mark.rutland, devicetree, alsa-devel, swarren, festevam, timur,
	rob.herring, tomasz.figa, shawn.guo, linuxppc-dev

Changelog:
v5->v6:
 * Sorted out rxtx clk source in DT binding.
 * Use devm_xxxx() functions.
 * Use platform_get_resource() instead.
v4->v5:
 * Dropped rx/tx-clksrc-names DT bindings.
 * Use standard clock binding instead to pass the clock source list.
 * Update the compatible list by using "imx35", the first SoC that has spdif.
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        |   56 +
 .../devicetree/bindings/sound/imx-audio-spdif.txt  |   29 +
 sound/soc/fsl/Kconfig                              |   14 +
 sound/soc/fsl/Makefile                             |    4 +
 sound/soc/fsl/fsl_spdif.c                          | 1272 ++++++++++++++++++++
 sound/soc/fsl/fsl_spdif.h                          |  224 ++++
 sound/soc/fsl/imx-spdif.c                          |  134 ++
 7 files changed, 1733 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 v6 2/2] ASoC: fsl: Add S/PDIF machine driver
From: Nicolin Chen @ 2013-08-16 12:56 UTC (permalink / raw)
  To: broonie, lars, p.zabel, s.hauer
  Cc: mark.rutland, devicetree, alsa-devel, swarren, festevam, timur,
	rob.herring, tomasz.figa, shawn.guo, linuxppc-dev
In-Reply-To: <cover.1376657643.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 v6 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Nicolin Chen @ 2013-08-16 12:56 UTC (permalink / raw)
  To: broonie, lars, p.zabel, s.hauer
  Cc: mark.rutland, devicetree, alsa-devel, swarren, festevam, timur,
	rob.herring, tomasz.figa, shawn.guo, linuxppc-dev
In-Reply-To: <cover.1376657643.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        |   56 +
 sound/soc/fsl/Kconfig                              |    3 +
 sound/soc/fsl/Makefile                             |    2 +
 sound/soc/fsl/fsl_spdif.c                          | 1272 ++++++++++++++++++++
 sound/soc/fsl/fsl_spdif.h                          |  224 ++++
 5 files changed, 1557 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..5549ce3
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
@@ -0,0 +1,56 @@
+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".
+
+  - 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		comments
+	"core"		The core clock of spdif controller
+	"rxtx<0-7>"	Clock source list for tx and rx clock.
+			This clock list should be identical to
+			the source list connecting to the spdif
+			clock mux in "SPDIF Transceiver Clock
+			Diagram" of SoC reference manual. It
+			can also be referred to TxClk_Source
+			bit of register SPDIF_STC.
+
+Example:
+
+spdif: spdif@02004000 {
+	compatible = "fsl,imx6q-spdif",
+		"fsl,imx35-spdif";
+	reg = <0x02004000 0x4000>;
+	interrupts = <0 52 0x04>;
+	dmas = <&sdma 14 18 0>,
+	       <&sdma 15 18 0>;
+	dma-names = "rx", "tx";
+
+	clocks = <&clks 197>, <&clks 3>,
+	       <&clks 197>, <&clks 107>,
+	       <&clks 0>, <&clks 118>,
+	       <&clks 62>, <&clks 139>,
+	       <&clks 0>;
+	clock-names = "core", "rxtx0",
+		"rxtx1", "rxtx2",
+		"rxtx3", "rxtx4",
+		"rxtx5", "rxtx6",
+		"rxtx7";
+
+	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..e00125e
--- /dev/null
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -0,0 +1,1272 @@
+/*
+ * 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)
+
+/* Index list for the values that has if (DPLL Locked) condition */
+static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb, };
+#define SRPC_NODPLL_START1	0x5
+#define SRPC_NODPLL_START2	0xc
+
+/*
+ * 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;
+	u8 txclk_div[SPDIF_TXRATE_MAX];
+	u8 txclk_src[SPDIF_TXRATE_MAX];
+	u8 rxclk_src;
+	struct clk *txclk[SPDIF_TXRATE_MAX];
+	struct clk *rxclk;
+	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;
+
+	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 csfs = 0;
+	u8 clk = -1, div = 1;
+	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];
+	div = spdif_priv->txclk_div[rate];
+
+	/*
+	 * 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 (srpc_dpll_locked[clksrc] && (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 u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
+				struct clk *clk, u64 savesub,
+				enum spdif_txrate index)
+{
+	const u32 rate[] = { 32000, 44100, 48000, };
+	u64 rate_ideal, rate_actual, sub;
+	u32 div, arate;
+
+	for (div = 1; div <= 128; div++) {
+		rate_ideal = rate[index] * (div + 1) * 64;
+		rate_actual = clk_round_rate(clk, rate_ideal);
+
+		arate = rate_actual / 64;
+		arate /= div;
+
+		if (arate == rate[index]) {
+			/* We are lucky */
+			savesub = 0;
+			spdif_priv->txclk_div[index] = div;
+			break;
+		} else if (arate / rate[index] == 1) {
+			/* A little bigger than expect */
+			sub = (arate - rate[index]) * 100000;
+			do_div(sub, rate[index]);
+			if (sub < savesub) {
+				savesub = sub;
+				spdif_priv->txclk_div[index] = div;
+			}
+		} else if (rate[index] / arate == 1) {
+			/* A little smaller than expect */
+			sub = (rate[index] - arate) * 100000;
+			do_div(sub, rate[index]);
+			if (sub < savesub) {
+				savesub = sub;
+				spdif_priv->txclk_div[index] = div;
+			}
+		}
+	}
+
+	return savesub;
+}
+
+static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
+				enum spdif_txrate index)
+{
+	const u32 rate[] = { 32000, 44100, 48000, };
+	struct platform_device *pdev = spdif_priv->pdev;
+	struct device *dev = &pdev->dev;
+	u64 savesub = 100000, ret;
+	struct clk *clk;
+	char tmp[16];
+	int i;
+
+	for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
+		sprintf(tmp, "rxtx%d", i);
+		clk = devm_clk_get(&pdev->dev, tmp);
+		if (IS_ERR(clk)) {
+			dev_err(dev, "no rxtx%d property in devicetree\n", i);
+			return PTR_ERR(clk);
+		}
+		if (!clk_get_rate(clk))
+			continue;
+
+		ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index);
+		if (savesub == ret)
+			continue;
+
+		savesub = ret;
+		spdif_priv->txclk[index] = clk;
+		spdif_priv->txclk_src[index] = i;
+
+		/* To quick catch a divisor, we allow a 0.1% deviation */
+		if (savesub < 100)
+			break;
+	}
+
+	dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate",
+			spdif_priv->txclk_src[index], rate[index]);
+	dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate",
+			spdif_priv->txclk_div[index], rate[index]);
+
+	return 0;
+}
+
+static int fsl_spdif_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct fsl_spdif_priv *spdif_priv;
+	struct spdif_mixer_control *ctrl;
+	struct resource *res;
+	void __iomem *regs;
+	int irq, ret, i;
+
+	if (!np)
+		return -ENODEV;
+
+	spdif_priv = devm_kzalloc(&pdev->dev,
+			sizeof(struct fsl_spdif_priv) + strlen(np->name) + 1, GFP_KERNEL);
+	if (!spdif_priv) {
+		dev_err(&pdev->dev, "could not allocate DAI object\n");
+		return -ENOMEM;
+	}
+
+	strcpy(spdif_priv->name, np->name);
+
+	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 */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (IS_ERR(res)) {
+		dev_err(&pdev->dev, "could not determine device resources\n");
+		return PTR_ERR(res);
+	}
+
+	regs = devm_ioremap_resource(&pdev->dev, res);
+	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");
+		return PTR_ERR(spdif_priv->regmap);
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
+		return irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0,
+			spdif_priv->name, spdif_priv);
+	if (ret) {
+		dev_err(&pdev->dev, "could not claim irq %u\n", irq);
+		return ret;
+	}
+
+	/* Select clock source for rx/tx clock */
+	spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
+	if (IS_ERR(spdif_priv->rxclk)) {
+		dev_err(&pdev->dev, "no rxtx1 property in devicetree\n");
+		return PTR_ERR(spdif_priv->rxclk);
+	}
+	spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC;
+
+	for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+		ret = fsl_spdif_probe_txclk(spdif_priv, i);
+		if (ret)
+			return ret;
+	}
+
+	/* Prepare rx/tx clock */
+	clk_prepare(spdif_priv->rxclk);
+	for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+		clk_prepare(spdif_priv->txclk[i]);
+
+	/* initial spinlock for control data */
+	ctrl = &spdif_priv->fsl_spdif_control;
+	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);
+	for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+		clk_unprepare(spdif_priv->txclk[i]);
+	clk_unprepare(spdif_priv->rxclk);
+
+	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++)
+		clk_unprepare(spdif_priv->txclk[i]);
+	clk_unprepare(spdif_priv->rxclk);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id fsl_spdif_dt_ids[] = {
+	{ .compatible = "fsl,imx35-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-dai");
diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h
new file mode 100644
index 0000000..f8357f6
--- /dev/null
+++ b/sound/soc/fsl/fsl_spdif.h
@@ -0,0 +1,224 @@
+/*
+ * 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_OFFSET1	5
+#define SRPC_CLKSRC_SEL_LOCKED_OFFSET2	2
+#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_1
+
+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

* [RFC PATCH v2 00/11] Add (de)compression support to pstore
From: Aruna Balakrishnaiah @ 2013-08-16 13:17 UTC (permalink / raw)
  To: linuxppc-dev, tony.luck, linux-kernel, keescook
  Cc: jkenisto, cbouatmailru, mahesh, ccross

Changes from v1:
	- Allocate compression workspace during initialisation as
	per Tony's suggestion
         - Copy the recent messages from big_oops_buf to psinfo->buf
	when compression fails, since the printk buffer
        would be fetched for compression calling it again when
        compression fails would have moved the iterator of
        printk buffer which results in fetching old contents.

The patchset adds compression and decompression support to pstore.

As the non-volatile storage space is limited, adding compression
support results in capturing more data within limited space.

Size of dmesg file in a powerpc/pseries box with nvram's
oops partition (to store oops-log) size 4k:

Without compression:
dmesg-nvram-1:  ~ 4k (3980)
WIth compression:
dmesg-nvram-1: ~8.8k (8844)

Writing to persistent store
----------------------------
Compression will reduce the size of oops/panic report to atmost 45% of its
original size. (Based on experiments done while providing compression support
to nvram by Jim keniston).
Hence buffer of size ( (100/45 approx 2.22) *<registered_buffer> is allocated). 
The compression parameters selected based on some experiments:
compression_level = 6, window_bits = 12, memory_level = 4  achieved a
significant compression.
Data is compressed from the bigger buffer to registered buffer which is
returned to backends.
Pstore will indicate that with a flag 'compressed' which is passed to backends.
Using this flag, backends will add a flag in their header to indicate the data
is compressed or not while writing to persistent store.


Reading from persistent store
-----------------------------
When backends read data from persistent store it will use the flag added by it
while writing to persistent store to determine if the data is compressed or not.
Using the information, it will set the flag in pstore's read call back.
Pstore will decompress the data based on the flag and writes decompressed data
to the file.

Test results:
Have tested the patches on powerpc/pseries.

Needs testing with erst backend, efivars and persistent ram.


---

Aruna Balakrishnaiah (11):
      powerpc/pseries: Remove (de)compression in nvram with pstore enabled
      pstore: Add new argument 'compressed' in pstore write callback
      pstore/Kconfig: Select ZLIB_DEFLATE and ZLIB_INFLATE when PSTORE is selected
      pstore: Add compression support to pstore
      pstore: Introduce new argument 'compressed' in the read callback
      pstore: Add decompression support to pstore
      pstore: Add file extension to pstore file if compressed
      powerpc/pseries: Read and write to the 'compressed' flag of pstore
      erst: Read and write to the 'compressed' flag of pstore
      efi-pstore: Read and write to the 'compressed' flag of pstore
      pstore/ram: Read and write to the 'compressed' flag of pstore


 arch/powerpc/platforms/pseries/nvram.c |  112 +++--------------
 drivers/acpi/apei/erst.c               |   21 ++-
 drivers/firmware/efi/efi-pstore.c      |   27 +++-
 fs/pstore/Kconfig                      |    2 
 fs/pstore/inode.c                      |    7 +
 fs/pstore/internal.h                   |    5 -
 fs/pstore/platform.c                   |  212 ++++++++++++++++++++++++++++++--
 fs/pstore/ram.c                        |   41 +++++-
 include/linux/pstore.h                 |    6 -
 9 files changed, 299 insertions(+), 134 deletions(-)

-- 

^ permalink raw reply

* [RFC PATCH v2 01/11] powerpc/pseries: Remove (de)compression in nvram with pstore enabled
From: Aruna Balakrishnaiah @ 2013-08-16 13:17 UTC (permalink / raw)
  To: linuxppc-dev, tony.luck, linux-kernel, keescook
  Cc: jkenisto, cbouatmailru, mahesh, ccross
In-Reply-To: <20130816131403.3338.82330.stgit@aruna-ThinkPad-T420>

(De)compression support is provided in pstore in subsequent patches which
needs an additional argument 'compressed' to determine if the data
is compressed or not. This patch will take care of removing (de)compression
in nvram with pstore which was making use of 'hsize' argument in pstore write
as 'hsize' will be removed in the subsequent patch.

Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
---
 arch/powerpc/platforms/pseries/nvram.c |  102 ++++----------------------------
 1 file changed, 12 insertions(+), 90 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 6a5f2b1..b966458 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -539,36 +539,6 @@ static int zip_oops(size_t text_len)
 }
 
 #ifdef CONFIG_PSTORE
-/* Derived from logfs_uncompress */
-int nvram_decompress(void *in, void *out, size_t inlen, size_t outlen)
-{
-	int err, ret;
-
-	ret = -EIO;
-	err = zlib_inflateInit(&stream);
-	if (err != Z_OK)
-		goto error;
-
-	stream.next_in = in;
-	stream.avail_in = inlen;
-	stream.total_in = 0;
-	stream.next_out = out;
-	stream.avail_out = outlen;
-	stream.total_out = 0;
-
-	err = zlib_inflate(&stream, Z_FINISH);
-	if (err != Z_STREAM_END)
-		goto error;
-
-	err = zlib_inflateEnd(&stream);
-	if (err != Z_OK)
-		goto error;
-
-	ret = stream.total_out;
-error:
-	return ret;
-}
-
 static int nvram_pstore_open(struct pstore_info *psi)
 {
 	/* Reset the iterator to start reading partitions again */
@@ -611,30 +581,8 @@ static int nvram_pstore_write(enum pstore_type_id type,
 	oops_hdr->report_length = (u16) size;
 	oops_hdr->timestamp = get_seconds();
 
-	if (big_oops_buf) {
-		rc = zip_oops(size);
-		/*
-		 * If compression fails copy recent log messages from
-		 * big_oops_buf to oops_data.
-		 */
-		if (rc != 0) {
-			size_t diff = size - oops_data_sz + hsize;
-
-			if (size > oops_data_sz) {
-				memcpy(oops_data, big_oops_buf, hsize);
-				memcpy(oops_data + hsize, big_oops_buf + diff,
-					oops_data_sz - hsize);
-
-				oops_hdr->report_length = (u16) oops_data_sz;
-			} else
-				memcpy(oops_data, big_oops_buf, size);
-		} else
-			err_type = ERR_TYPE_KERNEL_PANIC_GZ;
-	}
-
 	rc = nvram_write_os_partition(&oops_log_partition, oops_buf,
-		(int) (sizeof(*oops_hdr) + oops_hdr->report_length), err_type,
-		count);
+		(int) (sizeof(*oops_hdr) + size), err_type, count);
 
 	if (rc != 0)
 		return rc;
@@ -655,7 +603,7 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
 	struct oops_log_info *oops_hdr;
 	unsigned int err_type, id_no, size = 0;
 	struct nvram_os_partition *part = NULL;
-	char *buff = NULL, *big_buff = NULL;
+	char *buff = NULL;
 	int sig = 0;
 	loff_t p;
 
@@ -719,8 +667,7 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
 		*id = id_no;
 
 	if (nvram_type_ids[read_type] == PSTORE_TYPE_DMESG) {
-		int length, unzipped_len;
-		size_t hdr_size;
+		size_t length, hdr_size;
 
 		oops_hdr = (struct oops_log_info *)buff;
 		if (oops_hdr->version < OOPS_HDR_VERSION) {
@@ -740,24 +687,6 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
 			return -ENOMEM;
 		memcpy(*buf, buff + hdr_size, length);
 		kfree(buff);
-
-		if (err_type == ERR_TYPE_KERNEL_PANIC_GZ) {
-			big_buff = kmalloc(big_oops_buf_sz, GFP_KERNEL);
-			if (!big_buff)
-				return -ENOMEM;
-
-			unzipped_len = nvram_decompress(*buf, big_buff,
-						length, big_oops_buf_sz);
-
-			if (unzipped_len < 0) {
-				pr_err("nvram: decompression failed, returned "
-					"rc %d\n", unzipped_len);
-				kfree(big_buff);
-			} else {
-				*buf = big_buff;
-				length = unzipped_len;
-			}
-		}
 		return length;
 	}
 
@@ -777,13 +706,8 @@ static int nvram_pstore_init(void)
 {
 	int rc = 0;
 
-	if (big_oops_buf) {
-		nvram_pstore_info.buf = big_oops_buf;
-		nvram_pstore_info.bufsize = big_oops_buf_sz;
-	} else {
-		nvram_pstore_info.buf = oops_data;
-		nvram_pstore_info.bufsize = oops_data_sz;
-	}
+	nvram_pstore_info.buf = oops_data;
+	nvram_pstore_info.bufsize = oops_data_sz;
 
 	rc = pstore_register(&nvram_pstore_info);
 	if (rc != 0)
@@ -802,7 +726,6 @@ static int nvram_pstore_init(void)
 static void __init nvram_init_oops_partition(int rtas_partition_exists)
 {
 	int rc;
-	size_t size;
 
 	rc = pseries_nvram_init_os_partition(&oops_log_partition);
 	if (rc != 0) {
@@ -823,6 +746,11 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
 	oops_data = oops_buf + sizeof(struct oops_log_info);
 	oops_data_sz = oops_log_partition.size - sizeof(struct oops_log_info);
 
+	rc = nvram_pstore_init();
+
+	if (!rc)
+		return;
+
 	/*
 	 * Figure compression (preceded by elimination of each line's <n>
 	 * severity prefix) will reduce the oops/panic report to at most
@@ -831,9 +759,8 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
 	big_oops_buf_sz = (oops_data_sz * 100) / 45;
 	big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
 	if (big_oops_buf) {
-		size = max(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL),
-			zlib_inflate_workspacesize());
-		stream.workspace = kmalloc(size, GFP_KERNEL);
+		stream.workspace =  kmalloc(zlib_deflate_workspacesize(
+					WINDOW_BITS, MEM_LEVEL), GFP_KERNEL);
 		if (!stream.workspace) {
 			pr_err("nvram: No memory for compression workspace; "
 				"skipping compression of %s partition data\n",
@@ -847,11 +774,6 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists)
 		stream.workspace = NULL;
 	}
 
-	rc = nvram_pstore_init();
-
-	if (!rc)
-		return;
-
 	rc = kmsg_dump_register(&nvram_kmsg_dumper);
 	if (rc != 0) {
 		pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc);

^ permalink raw reply related

* [RFC PATCH v2 02/11] pstore: Add new argument 'compressed' in pstore write callback
From: Aruna Balakrishnaiah @ 2013-08-16 13:17 UTC (permalink / raw)
  To: linuxppc-dev, tony.luck, linux-kernel, keescook
  Cc: jkenisto, cbouatmailru, mahesh, ccross
In-Reply-To: <20130816131403.3338.82330.stgit@aruna-ThinkPad-T420>

Addition of new argument 'compressed' in the write call back will
help the backend to know if the data passed from pstore is compressed
or not (In case where compression fails.). If compressed, the backend
can add a tag indicating the data is compressed while writing to
persistent store.

Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
---
 arch/powerpc/platforms/pseries/nvram.c |    4 ++--
 drivers/acpi/apei/erst.c               |    4 ++--
 drivers/firmware/efi/efi-pstore.c      |    2 +-
 fs/pstore/platform.c                   |    7 ++++---
 fs/pstore/ram.c                        |    2 +-
 include/linux/pstore.h                 |    4 ++--
 6 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index b966458..dbe5dad 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -554,7 +554,7 @@ static int nvram_pstore_open(struct pstore_info *psi)
  * @part:               pstore writes data to registered buffer in parts,
  *                      part number will indicate the same.
  * @count:              Indicates oops count
- * @hsize:              Size of header added by pstore
+ * @compressed:         Flag to indicate the log is compressed
  * @size:               number of bytes written to the registered buffer
  * @psi:                registered pstore_info structure
  *
@@ -565,7 +565,7 @@ static int nvram_pstore_open(struct pstore_info *psi)
 static int nvram_pstore_write(enum pstore_type_id type,
 				enum kmsg_dump_reason reason,
 				u64 *id, unsigned int part, int count,
-				size_t hsize, size_t size,
+				bool compressed, size_t size,
 				struct pstore_info *psi)
 {
 	int rc;
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index 88d0b0f..5e90796 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -935,7 +935,7 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
 			   struct timespec *time, char **buf,
 			   struct pstore_info *psi);
 static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason,
-		       u64 *id, unsigned int part, int count, size_t hsize,
+		       u64 *id, unsigned int part, int count, bool compressed,
 		       size_t size, struct pstore_info *psi);
 static int erst_clearer(enum pstore_type_id type, u64 id, int count,
 			struct timespec time, struct pstore_info *psi);
@@ -1055,7 +1055,7 @@ out:
 }
 
 static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason,
-		       u64 *id, unsigned int part, int count, size_t hsize,
+		       u64 *id, unsigned int part, int count, bool compressed,
 		       size_t size, struct pstore_info *psi)
 {
 	struct cper_pstore_record *rcd = (struct cper_pstore_record *)
diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c
index 73de5a9..fab6892 100644
--- a/drivers/firmware/efi/efi-pstore.c
+++ b/drivers/firmware/efi/efi-pstore.c
@@ -103,7 +103,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
 
 static int efi_pstore_write(enum pstore_type_id type,
 		enum kmsg_dump_reason reason, u64 *id,
-		unsigned int part, int count, size_t hsize, size_t size,
+		unsigned int part, int count, bool compressed, size_t size,
 		struct pstore_info *psi)
 {
 	char name[DUMP_NAME_LEN];
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 422962a..20fa686 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -149,6 +149,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
 		unsigned long size;
 		int hsize;
 		size_t len;
+		bool compressed = false;
 
 		dst = psinfo->buf;
 		hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
@@ -159,7 +160,7 @@ static void pstore_dump(struct kmsg_dumper *dumper,
 			break;
 
 		ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part,
-				    oopscount, hsize, hsize + len, psinfo);
+				    oopscount, compressed, hsize + len, psinfo);
 		if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
 			pstore_new_entry = 1;
 
@@ -221,10 +222,10 @@ static void pstore_register_console(void) {}
 static int pstore_write_compat(enum pstore_type_id type,
 			       enum kmsg_dump_reason reason,
 			       u64 *id, unsigned int part, int count,
-			       size_t hsize, size_t size,
+			       bool compressed, size_t size,
 			       struct pstore_info *psi)
 {
-	return psi->write_buf(type, reason, id, part, psinfo->buf, hsize,
+	return psi->write_buf(type, reason, id, part, psinfo->buf, compressed,
 			     size, psi);
 }
 
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c
index a6119f9..fe7188f 100644
--- a/fs/pstore/ram.c
+++ b/fs/pstore/ram.c
@@ -196,7 +196,7 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
 					    enum kmsg_dump_reason reason,
 					    u64 *id, unsigned int part,
 					    const char *buf,
-					    size_t hsize, size_t size,
+					    bool compressed, size_t size,
 					    struct pstore_info *psi)
 {
 	struct ramoops_context *cxt = psi->data;
diff --git a/include/linux/pstore.h b/include/linux/pstore.h
index 4aa80ba..abfca4f 100644
--- a/include/linux/pstore.h
+++ b/include/linux/pstore.h
@@ -58,11 +58,11 @@ struct pstore_info {
 			struct pstore_info *psi);
 	int		(*write)(enum pstore_type_id type,
 			enum kmsg_dump_reason reason, u64 *id,
-			unsigned int part, int count, size_t hsize,
+			unsigned int part, int count, bool compressed,
 			size_t size, struct pstore_info *psi);
 	int		(*write_buf)(enum pstore_type_id type,
 			enum kmsg_dump_reason reason, u64 *id,
-			unsigned int part, const char *buf, size_t hsize,
+			unsigned int part, const char *buf, bool compressed,
 			size_t size, struct pstore_info *psi);
 	int		(*erase)(enum pstore_type_id type, u64 id,
 			int count, struct timespec time,

^ permalink raw reply related

* [RFC PATCH v2 03/11] pstore/Kconfig: Select ZLIB_DEFLATE and ZLIB_INFLATE when PSTORE is selected
From: Aruna Balakrishnaiah @ 2013-08-16 13:17 UTC (permalink / raw)
  To: linuxppc-dev, tony.luck, linux-kernel, keescook
  Cc: jkenisto, cbouatmailru, mahesh, ccross
In-Reply-To: <20130816131403.3338.82330.stgit@aruna-ThinkPad-T420>

Pstore will make use of deflate and inflate algorithm to compress and decompress
the data. So when Pstore is enabled select zlib_deflate and zlib_inflate.

Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
---
 fs/pstore/Kconfig |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig
index ca71db6..983d951 100644
--- a/fs/pstore/Kconfig
+++ b/fs/pstore/Kconfig
@@ -1,6 +1,8 @@
 config PSTORE
 	bool "Persistent store support"
 	default n
+	select ZLIB_DEFLATE
+	select ZLIB_INFLATE
 	help
 	   This option enables generic access to platform level
 	   persistent storage via "pstore" filesystem that can

^ permalink raw reply related

* [RFC PATCH v2 04/11] pstore: Add compression support to pstore
From: Aruna Balakrishnaiah @ 2013-08-16 13:18 UTC (permalink / raw)
  To: linuxppc-dev, tony.luck, linux-kernel, keescook
  Cc: jkenisto, cbouatmailru, mahesh, ccross
In-Reply-To: <20130816131403.3338.82330.stgit@aruna-ThinkPad-T420>

Add compression support to pstore which will help in capturing more data.
Initially, pstore will make a call to kmsg_dump with a bigger buffer
and will pass the size of bigger buffer to kmsg_dump and then compress
the data to registered buffer of registered size.

In case compression fails, pstore will capture the uncompressed
data by making a call again to kmsg_dump with registered_buffer
of registered size.

Pstore will indicate the data is compressed or not with a flag
in the write callback.

Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
---
 fs/pstore/platform.c |  148 +++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 139 insertions(+), 9 deletions(-)

diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 20fa686..56218cb 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -26,6 +26,7 @@
 #include <linux/console.h>
 #include <linux/module.h>
 #include <linux/pstore.h>
+#include <linux/zlib.h>
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/slab.h>
@@ -65,6 +66,15 @@ struct pstore_info *psinfo;
 
 static char *backend;
 
+/* Compression parameters */
+#define COMPR_LEVEL 6
+#define WINDOW_BITS 12
+#define MEM_LEVEL 4
+static struct z_stream_s stream;
+
+static char *big_oops_buf;
+static size_t big_oops_buf_sz;
+
 /* How much of the console log to snapshot */
 static unsigned long kmsg_bytes = 10240;
 
@@ -117,6 +127,91 @@ bool pstore_cannot_block_path(enum kmsg_dump_reason reason)
 }
 EXPORT_SYMBOL_GPL(pstore_cannot_block_path);
 
+/* Derived from logfs_compress() */
+static int pstore_compress(const void *in, void *out, size_t inlen,
+							size_t outlen)
+{
+	int err, ret;
+
+	ret = -EIO;
+	err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS,
+						MEM_LEVEL, Z_DEFAULT_STRATEGY);
+	if (err != Z_OK)
+		goto error;
+
+	stream.next_in = in;
+	stream.avail_in = inlen;
+	stream.total_in = 0;
+	stream.next_out = out;
+	stream.avail_out = outlen;
+	stream.total_out = 0;
+
+	err = zlib_deflate(&stream, Z_FINISH);
+	if (err != Z_STREAM_END)
+		goto error;
+
+	err = zlib_deflateEnd(&stream);
+	if (err != Z_OK)
+		goto error;
+
+	if (stream.total_out >= stream.total_in)
+		goto error;
+
+	ret = stream.total_out;
+error:
+	return ret;
+}
+
+static void allocate_buf_for_compression(void)
+{
+	size_t size;
+
+	big_oops_buf_sz = (psinfo->bufsize * 100) / 45;
+	big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL);
+	if (big_oops_buf) {
+		size = max(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL),
+			zlib_inflate_workspacesize());
+		stream.workspace = kmalloc(size, GFP_KERNEL);
+		if (!stream.workspace) {
+			pr_err("pstore: No memory for compression workspace; "
+				"skipping compression\n");
+			kfree(big_oops_buf);
+			big_oops_buf = NULL;
+		}
+	} else {
+		pr_err("No memory for uncompressed data; "
+			"skipping compression\n");
+		stream.workspace = NULL;
+	}
+
+}
+
+/*
+ * Called when compression fails, since the printk buffer
+ * would be fetched for compression calling it again when
+ * compression fails would have moved the iterator of
+ * printk buffer which results in fetching old contents.
+ * Copy the recent messages from big_oops_buf to psinfo->buf
+ */
+static size_t copy_kmsg_to_buffer(int hsize, size_t len)
+{
+	size_t total_len;
+	size_t diff;
+
+	total_len = hsize + len;
+
+	if (total_len > psinfo->bufsize) {
+		diff = total_len - psinfo->bufsize + hsize;
+		memcpy(psinfo->buf, big_oops_buf, hsize);
+		memcpy(psinfo->buf + hsize, big_oops_buf + diff,
+					psinfo->bufsize - hsize);
+		total_len = psinfo->bufsize;
+	} else
+		memcpy(psinfo->buf, big_oops_buf, total_len);
+
+	return total_len;
+}
+
 /*
  * callback from kmsg_dump. (s2,l2) has the most recently
  * written bytes, older bytes are in (s1,l1). Save as much
@@ -148,23 +243,56 @@ static void pstore_dump(struct kmsg_dumper *dumper,
 		char *dst;
 		unsigned long size;
 		int hsize;
+		int zipped_len = -1;
 		size_t len;
-		bool compressed = false;
+		bool compressed;
+		size_t total_len;
 
-		dst = psinfo->buf;
-		hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part);
-		size = psinfo->bufsize - hsize;
-		dst += hsize;
+		if (big_oops_buf) {
+			dst = big_oops_buf;
+			hsize = sprintf(dst, "%s#%d Part%d\n", why,
+							oopscount, part);
+			size = big_oops_buf_sz - hsize;
 
-		if (!kmsg_dump_get_buffer(dumper, true, dst, size, &len))
-			break;
+			if (!kmsg_dump_get_buffer(dumper, true, dst + hsize,
+								size, &len))
+				break;
+
+			zipped_len = pstore_compress(dst, psinfo->buf,
+						hsize + len, psinfo->bufsize);
+
+			if (zipped_len > 0) {
+				compressed = true;
+				total_len = zipped_len;
+			} else {
+				pr_err("pstore: compression failed for Part %d"
+					" returned %d\n", part, zipped_len);
+				pr_err("pstore: Capture uncompressed"
+					" oops/panic report of Part %d\n", part);
+				compressed = false;
+				total_len = copy_kmsg_to_buffer(hsize, len);
+			}
+		} else {
+			dst = psinfo->buf;
+			hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount,
+									part);
+			size = psinfo->bufsize - hsize;
+			dst += hsize;
+
+			if (!kmsg_dump_get_buffer(dumper, true, dst,
+								size, &len))
+				break;
+
+			compressed = false;
+			total_len = hsize + len;
+		}
 
 		ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part,
-				    oopscount, compressed, hsize + len, psinfo);
+				    oopscount, compressed, total_len, psinfo);
 		if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted())
 			pstore_new_entry = 1;
 
-		total += hsize + len;
+		total += total_len;
 		part++;
 	}
 	if (pstore_cannot_block_path(reason)) {
@@ -262,6 +390,8 @@ int pstore_register(struct pstore_info *psi)
 		return -EINVAL;
 	}
 
+	allocate_buf_for_compression();
+
 	if (pstore_is_mounted())
 		pstore_get_records(0);
 

^ permalink raw reply related

* [RFC PATCH v2 05/11] pstore: Introduce new argument 'compressed' in the read callback
From: Aruna Balakrishnaiah @ 2013-08-16 13:18 UTC (permalink / raw)
  To: linuxppc-dev, tony.luck, linux-kernel, keescook
  Cc: jkenisto, cbouatmailru, mahesh, ccross
In-Reply-To: <20130816131403.3338.82330.stgit@aruna-ThinkPad-T420>

Backends will set the flag 'compressed' after reading the log from
persistent store to indicate the data being returned to pstore is
compressed or not.

Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
---
 arch/powerpc/platforms/pseries/nvram.c |    2 +-
 drivers/acpi/apei/erst.c               |    4 ++--
 drivers/firmware/efi/efi-pstore.c      |    3 ++-
 fs/pstore/platform.c                   |    4 +++-
 fs/pstore/ram.c                        |    3 ++-
 include/linux/pstore.h                 |    2 +-
 6 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index dbe5dad..6c4dc52a 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -598,7 +598,7 @@ static int nvram_pstore_write(enum pstore_type_id type,
  */
 static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
 				int *count, struct timespec *time, char **buf,
-				struct pstore_info *psi)
+				bool *compressed, struct pstore_info *psi)
 {
 	struct oops_log_info *oops_hdr;
 	unsigned int err_type, id_no, size = 0;
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index 5e90796..b0dca8e 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -933,7 +933,7 @@ static int erst_open_pstore(struct pstore_info *psi);
 static int erst_close_pstore(struct pstore_info *psi);
 static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
 			   struct timespec *time, char **buf,
-			   struct pstore_info *psi);
+			   bool *compressed, struct pstore_info *psi);
 static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason,
 		       u64 *id, unsigned int part, int count, bool compressed,
 		       size_t size, struct pstore_info *psi);
@@ -989,7 +989,7 @@ static int erst_close_pstore(struct pstore_info *psi)
 
 static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count,
 			   struct timespec *time, char **buf,
-			   struct pstore_info *psi)
+			   bool *compressed, struct pstore_info *psi)
 {
 	int rc;
 	ssize_t len = 0;
diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c
index fab6892..9a5425f 100644
--- a/drivers/firmware/efi/efi-pstore.c
+++ b/drivers/firmware/efi/efi-pstore.c
@@ -87,7 +87,8 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
 
 static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
 			       int *count, struct timespec *timespec,
-			       char **buf, struct pstore_info *psi)
+			       char **buf, bool *compressed,
+			       struct pstore_info *psi)
 {
 	struct pstore_read_data data;
 
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 56218cb..6418eb7 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -428,6 +428,7 @@ void pstore_get_records(int quiet)
 	enum pstore_type_id	type;
 	struct timespec		time;
 	int			failed = 0, rc;
+	bool			compressed;
 
 	if (!psi)
 		return;
@@ -436,7 +437,8 @@ void pstore_get_records(int quiet)
 	if (psi->open && psi->open(psi))
 		goto out;
 
-	while ((size = psi->read(&id, &type, &count, &time, &buf, psi)) > 0) {
+	while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed,
+				psi)) > 0) {
 		rc = pstore_mkfile(type, psi->name, id, count, buf,
 				  (size_t)size, time, psi);
 		kfree(buf);
diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c
index fe7188f..2927223 100644
--- a/fs/pstore/ram.c
+++ b/fs/pstore/ram.c
@@ -133,7 +133,8 @@ ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max,
 
 static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
 				   int *count, struct timespec *time,
-				   char **buf, struct pstore_info *psi)
+				   char **buf, bool *compressed,
+				   struct pstore_info *psi)
 {
 	ssize_t size;
 	ssize_t ecc_notice_size;
diff --git a/include/linux/pstore.h b/include/linux/pstore.h
index abfca4f..abd437d 100644
--- a/include/linux/pstore.h
+++ b/include/linux/pstore.h
@@ -55,7 +55,7 @@ struct pstore_info {
 	int		(*close)(struct pstore_info *psi);
 	ssize_t		(*read)(u64 *id, enum pstore_type_id *type,
 			int *count, struct timespec *time, char **buf,
-			struct pstore_info *psi);
+			bool *compressed, struct pstore_info *psi);
 	int		(*write)(enum pstore_type_id type,
 			enum kmsg_dump_reason reason, u64 *id,
 			unsigned int part, int count, bool compressed,

^ permalink raw reply related

* [RFC PATCH v2 06/11] pstore: Add decompression support to pstore
From: Aruna Balakrishnaiah @ 2013-08-16 13:18 UTC (permalink / raw)
  To: linuxppc-dev, tony.luck, linux-kernel, keescook
  Cc: jkenisto, cbouatmailru, mahesh, ccross
In-Reply-To: <20130816131403.3338.82330.stgit@aruna-ThinkPad-T420>

Based on the flag 'compressed' set or not, pstore will decompress the
data returning a plain text file. If decompression fails for a particular
record it will have the compressed data in the file which can be
decompressed with 'openssl' command line tool.

Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
---
 fs/pstore/platform.c |   53 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 51 insertions(+), 2 deletions(-)

diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 6418eb7..0195cca0 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -162,6 +162,36 @@ error:
 	return ret;
 }
 
+/* Derived from logfs_uncompress */
+int pstore_decompress(void *in, void *out, size_t inlen, size_t outlen)
+{
+	int err, ret;
+
+	ret = -EIO;
+	err = zlib_inflateInit(&stream);
+	if (err != Z_OK)
+		goto error;
+
+	stream.next_in = in;
+	stream.avail_in = inlen;
+	stream.total_in = 0;
+	stream.next_out = out;
+	stream.avail_out = outlen;
+	stream.total_out = 0;
+
+	err = zlib_inflate(&stream, Z_FINISH);
+	if (err != Z_STREAM_END)
+		goto error;
+
+	err = zlib_inflateEnd(&stream);
+	if (err != Z_OK)
+		goto error;
+
+	ret = stream.total_out;
+error:
+	return ret;
+}
+
 static void allocate_buf_for_compression(void)
 {
 	size_t size;
@@ -429,6 +459,7 @@ void pstore_get_records(int quiet)
 	struct timespec		time;
 	int			failed = 0, rc;
 	bool			compressed;
+	int			unzipped_len = -1;
 
 	if (!psi)
 		return;
@@ -439,10 +470,28 @@ void pstore_get_records(int quiet)
 
 	while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed,
 				psi)) > 0) {
+		if (compressed && (type == PSTORE_TYPE_DMESG)) {
+			if (big_oops_buf)
+				unzipped_len = pstore_decompress(buf,
+							big_oops_buf, size,
+							big_oops_buf_sz);
+
+			if (unzipped_len > 0) {
+				buf = big_oops_buf;
+				size = unzipped_len;
+			} else {
+				pr_err("pstore: decompression failed;"
+					"returned %d\n", unzipped_len);
+			}
+		}
 		rc = pstore_mkfile(type, psi->name, id, count, buf,
 				  (size_t)size, time, psi);
-		kfree(buf);
-		buf = NULL;
+		if (unzipped_len < 0) {
+			/* Free buffer other than big oops */
+			kfree(buf);
+			buf = NULL;
+		} else
+			unzipped_len = -1;
 		if (rc && (rc != -EEXIST || !quiet))
 			failed++;
 	}

^ permalink raw reply related

* [RFC PATCH v2 08/11] powerpc/pseries: Read and write to the 'compressed' flag of pstore
From: Aruna Balakrishnaiah @ 2013-08-16 13:18 UTC (permalink / raw)
  To: linuxppc-dev, tony.luck, linux-kernel, keescook
  Cc: jkenisto, cbouatmailru, mahesh, ccross
In-Reply-To: <20130816131403.3338.82330.stgit@aruna-ThinkPad-T420>

If data returned from pstore is compressed, nvram's write callback
will add a flag ERR_TYPE_KERNEL_PANIC_GZ indicating the data is compressed
while writing to nvram. If the data read from nvram is compressed, nvram's
read callback will set the flag 'compressed'. The patch adds backward
compatibilty with old format oops header when reading from pstore.

Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
---
 arch/powerpc/platforms/pseries/nvram.c |    8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c
index 6c4dc52a..d276cd3 100644
--- a/arch/powerpc/platforms/pseries/nvram.c
+++ b/arch/powerpc/platforms/pseries/nvram.c
@@ -581,6 +581,9 @@ static int nvram_pstore_write(enum pstore_type_id type,
 	oops_hdr->report_length = (u16) size;
 	oops_hdr->timestamp = get_seconds();
 
+	if (compressed)
+		err_type = ERR_TYPE_KERNEL_PANIC_GZ;
+
 	rc = nvram_write_os_partition(&oops_log_partition, oops_buf,
 		(int) (sizeof(*oops_hdr) + size), err_type, count);
 
@@ -687,6 +690,11 @@ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type,
 			return -ENOMEM;
 		memcpy(*buf, buff + hdr_size, length);
 		kfree(buff);
+
+		if (err_type == ERR_TYPE_KERNEL_PANIC_GZ)
+			*compressed = true;
+		else
+			*compressed = false;
 		return length;
 	}
 

^ permalink raw reply related

* [RFC PATCH v2 09/11] erst: Read and write to the 'compressed' flag of pstore
From: Aruna Balakrishnaiah @ 2013-08-16 13:18 UTC (permalink / raw)
  To: linuxppc-dev, tony.luck, linux-kernel, keescook
  Cc: jkenisto, cbouatmailru, mahesh, ccross
In-Reply-To: <20130816131403.3338.82330.stgit@aruna-ThinkPad-T420>

In pstore write, set the section type to CPER_SECTION_TYPE_DMESG_COMPR
if the data is compressed. In pstore read, read the section type and
update the 'compressed' flag accordingly.

Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
---
 drivers/acpi/apei/erst.c |   13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c
index b0dca8e..62df189 100644
--- a/drivers/acpi/apei/erst.c
+++ b/drivers/acpi/apei/erst.c
@@ -956,6 +956,9 @@ static struct pstore_info erst_info = {
 #define CPER_SECTION_TYPE_DMESG						\
 	UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54,	\
 		0x94, 0x19, 0xeb, 0x12)
+#define CPER_SECTION_TYPE_DMESG_Z					\
+	UUID_LE(0x4f118707, 0x04dd, 0x4055, 0xb5, 0xdd, 0x95, 0x6d,	\
+		0x34, 0xdd, 0xfa, 0xc6)
 #define CPER_SECTION_TYPE_MCE						\
 	UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96,	\
 		0x04, 0x4a, 0x38, 0xfc)
@@ -1034,7 +1037,12 @@ skip:
 	}
 	memcpy(*buf, rcd->data, len - sizeof(*rcd));
 	*id = record_id;
+	*compressed = false;
 	if (uuid_le_cmp(rcd->sec_hdr.section_type,
+			CPER_SECTION_TYPE_DMESG_Z) == 0) {
+		*type = PSTORE_TYPE_DMESG;
+		*compressed = true;
+	} else if (uuid_le_cmp(rcd->sec_hdr.section_type,
 			CPER_SECTION_TYPE_DMESG) == 0)
 		*type = PSTORE_TYPE_DMESG;
 	else if (uuid_le_cmp(rcd->sec_hdr.section_type,
@@ -1085,7 +1093,10 @@ static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason,
 	rcd->sec_hdr.flags = CPER_SEC_PRIMARY;
 	switch (type) {
 	case PSTORE_TYPE_DMESG:
-		rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG;
+		if (compressed)
+			rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z;
+		else
+			rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG;
 		break;
 	case PSTORE_TYPE_MCE:
 		rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE;

^ permalink raw reply related

* [RFC PATCH v2 10/11] efi-pstore: Read and write to the 'compressed' flag of pstore
From: Aruna Balakrishnaiah @ 2013-08-16 13:18 UTC (permalink / raw)
  To: linuxppc-dev, tony.luck, linux-kernel, keescook
  Cc: jkenisto, cbouatmailru, mahesh, ccross
In-Reply-To: <20130816131403.3338.82330.stgit@aruna-ThinkPad-T420>

In pstore write, Efi will add a character 'C'(compressed) or
D'(decompressed) in its header while writing to persistent store.
In pstore read, read the header and update the 'compressed' flag
accordingly.

Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
---
 drivers/firmware/efi/efi-pstore.c |   22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c
index 9a5425f..5002d50 100644
--- a/drivers/firmware/efi/efi-pstore.c
+++ b/drivers/firmware/efi/efi-pstore.c
@@ -35,6 +35,7 @@ struct pstore_read_data {
 	enum pstore_type_id *type;
 	int *count;
 	struct timespec *timespec;
+	bool *compressed;
 	char **buf;
 };
 
@@ -42,7 +43,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
 {
 	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
 	struct pstore_read_data *cb_data = data;
-	char name[DUMP_NAME_LEN];
+	char name[DUMP_NAME_LEN], data_type;
 	int i;
 	int cnt;
 	unsigned int part;
@@ -54,12 +55,23 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
 	for (i = 0; i < DUMP_NAME_LEN; i++)
 		name[i] = entry->var.VariableName[i];
 
-	if (sscanf(name, "dump-type%u-%u-%d-%lu",
+	if (sscanf(name, "dump-type%u-%u-%d-%lu-%c",
+		   cb_data->type, &part, &cnt, &time, &data_type) == 5) {
+		*cb_data->id = part;
+		*cb_data->count = cnt;
+		cb_data->timespec->tv_sec = time;
+		cb_data->timespec->tv_nsec = 0;
+		if (data_type == 'C')
+			*cb_data->compressed = true;
+		else
+			*cb_data->compressed = false;
+	} else if (sscanf(name, "dump-type%u-%u-%d-%lu",
 		   cb_data->type, &part, &cnt, &time) == 4) {
 		*cb_data->id = part;
 		*cb_data->count = cnt;
 		cb_data->timespec->tv_sec = time;
 		cb_data->timespec->tv_nsec = 0;
+		*cb_data->compressed = false;
 	} else if (sscanf(name, "dump-type%u-%u-%lu",
 			  cb_data->type, &part, &time) == 3) {
 		/*
@@ -71,6 +83,7 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data)
 		*cb_data->count = 0;
 		cb_data->timespec->tv_sec = time;
 		cb_data->timespec->tv_nsec = 0;
+		*cb_data->compressed = false;
 	} else
 		return 0;
 
@@ -96,6 +109,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
 	data.type = type;
 	data.count = count;
 	data.timespec = timespec;
+	data.compressed = compressed;
 	data.buf = buf;
 
 	return __efivar_entry_iter(efi_pstore_read_func, &efivar_sysfs_list, &data,
@@ -112,8 +126,8 @@ static int efi_pstore_write(enum pstore_type_id type,
 	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
 	int i, ret = 0;
 
-	sprintf(name, "dump-type%u-%u-%d-%lu", type, part, count,
-		get_seconds());
+	sprintf(name, "dump-type%u-%u-%d-%lu-%c", type, part, count,
+		get_seconds(), compressed ? 'C' : 'D');
 
 	for (i = 0; i < DUMP_NAME_LEN; i++)
 		efi_name[i] = name[i];

^ permalink raw reply related

* [RFC PATCH v2 11/11] pstore/ram: Read and write to the 'compressed' flag of pstore
From: Aruna Balakrishnaiah @ 2013-08-16 13:19 UTC (permalink / raw)
  To: linuxppc-dev, tony.luck, linux-kernel, keescook
  Cc: jkenisto, cbouatmailru, mahesh, ccross
In-Reply-To: <20130816131403.3338.82330.stgit@aruna-ThinkPad-T420>

In pstore write, add character 'C'(compressed) or 'D'(decompressed)
in the header while writing to Ram persistent buffer. In pstore read,
read the header and update the 'compressed' flag accordingly.

Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
---
 fs/pstore/ram.c |   36 ++++++++++++++++++++++++++++--------
 1 file changed, 28 insertions(+), 8 deletions(-)

diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c
index 2927223..4027c20 100644
--- a/fs/pstore/ram.c
+++ b/fs/pstore/ram.c
@@ -131,6 +131,27 @@ ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max,
 	return prz;
 }
 
+static void ramoops_read_kmsg_hdr(char *buffer, struct timespec *time,
+				  bool *compressed)
+{
+	char data_type;
+
+	if (sscanf(buffer, RAMOOPS_KERNMSG_HDR "%lu.%lu-%c\n",
+			&time->tv_sec, &time->tv_nsec, &data_type) == 3) {
+		if (data_type == 'C')
+			*compressed = true;
+		else
+			*compressed = false;
+	} else if (sscanf(buffer, RAMOOPS_KERNMSG_HDR "%lu.%lu\n",
+			&time->tv_sec, &time->tv_nsec) == 2) {
+			*compressed = false;
+	} else {
+		time->tv_sec = 0;
+		time->tv_nsec = 0;
+		*compressed = false;
+	}
+}
+
 static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
 				   int *count, struct timespec *time,
 				   char **buf, bool *compressed,
@@ -153,10 +174,6 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
 	if (!prz)
 		return 0;
 
-	/* TODO(kees): Bogus time for the moment. */
-	time->tv_sec = 0;
-	time->tv_nsec = 0;
-
 	size = persistent_ram_old_size(prz);
 
 	/* ECC correction notice */
@@ -167,12 +184,14 @@ static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type,
 		return -ENOMEM;
 
 	memcpy(*buf, persistent_ram_old(prz), size);
+	ramoops_read_kmsg_hdr(*buf, time, compressed);
 	persistent_ram_ecc_string(prz, *buf + size, ecc_notice_size + 1);
 
 	return size + ecc_notice_size;
 }
 
-static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz)
+static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz,
+				     bool compressed)
 {
 	char *hdr;
 	struct timespec timestamp;
@@ -183,8 +202,9 @@ static size_t ramoops_write_kmsg_hdr(struct persistent_ram_zone *prz)
 		timestamp.tv_sec = 0;
 		timestamp.tv_nsec = 0;
 	}
-	hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu\n",
-		(long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000));
+	hdr = kasprintf(GFP_ATOMIC, RAMOOPS_KERNMSG_HDR "%lu.%lu-%c\n",
+		(long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000),
+		compressed ? 'C' : 'D');
 	WARN_ON_ONCE(!hdr);
 	len = hdr ? strlen(hdr) : 0;
 	persistent_ram_write(prz, hdr, len);
@@ -243,7 +263,7 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type,
 
 	prz = cxt->przs[cxt->dump_write_cnt];
 
-	hlen = ramoops_write_kmsg_hdr(prz);
+	hlen = ramoops_write_kmsg_hdr(prz, compressed);
 	if (size + hlen > prz->buffer_size)
 		size = prz->buffer_size - hlen;
 	persistent_ram_write(prz, buf, size);

^ permalink raw reply related

* [RFC PATCH v2 07/11] pstore: Add file extension to pstore file if compressed
From: Aruna Balakrishnaiah @ 2013-08-16 13:18 UTC (permalink / raw)
  To: linuxppc-dev, tony.luck, linux-kernel, keescook
  Cc: jkenisto, cbouatmailru, mahesh, ccross
In-Reply-To: <20130816131403.3338.82330.stgit@aruna-ThinkPad-T420>

In case decompression fails, add a ".enc.z" to indicate the file has
compressed data. This will help user space utilities to figure
out the file contents.

Signed-off-by: Aruna Balakrishnaiah <aruna@linux.vnet.ibm.com>
---
 fs/pstore/inode.c    |    7 ++++---
 fs/pstore/internal.h |    5 +++--
 fs/pstore/platform.c |    4 +++-
 3 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c
index 71bf5f4..519d278 100644
--- a/fs/pstore/inode.c
+++ b/fs/pstore/inode.c
@@ -275,8 +275,8 @@ int pstore_is_mounted(void)
  * Set the mtime & ctime to the date that this record was originally stored.
  */
 int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
-		  char *data, size_t size, struct timespec time,
-		  struct pstore_info *psi)
+		  char *data, bool compressed, size_t size,
+		  struct timespec time, struct pstore_info *psi)
 {
 	struct dentry		*root = pstore_sb->s_root;
 	struct dentry		*dentry;
@@ -315,7 +315,8 @@ int pstore_mkfile(enum pstore_type_id type, char *psname, u64 id, int count,
 
 	switch (type) {
 	case PSTORE_TYPE_DMESG:
-		sprintf(name, "dmesg-%s-%lld", psname, id);
+		sprintf(name, "dmesg-%s-%lld%s", psname, id,
+						compressed ? ".enc.z" : "");
 		break;
 	case PSTORE_TYPE_CONSOLE:
 		sprintf(name, "console-%s", psname);
diff --git a/fs/pstore/internal.h b/fs/pstore/internal.h
index 937d820..3b3d305 100644
--- a/fs/pstore/internal.h
+++ b/fs/pstore/internal.h
@@ -50,8 +50,9 @@ extern struct pstore_info *psinfo;
 extern void	pstore_set_kmsg_bytes(int);
 extern void	pstore_get_records(int);
 extern int	pstore_mkfile(enum pstore_type_id, char *psname, u64 id,
-			      int count, char *data, size_t size,
-			      struct timespec time, struct pstore_info *psi);
+			      int count, char *data, bool compressed,
+			      size_t size, struct timespec time,
+			      struct pstore_info *psi);
 extern int	pstore_is_mounted(void);
 
 #endif
diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c
index 0195cca0..cf0b53f 100644
--- a/fs/pstore/platform.c
+++ b/fs/pstore/platform.c
@@ -479,13 +479,15 @@ void pstore_get_records(int quiet)
 			if (unzipped_len > 0) {
 				buf = big_oops_buf;
 				size = unzipped_len;
+				compressed = false;
 			} else {
 				pr_err("pstore: decompression failed;"
 					"returned %d\n", unzipped_len);
+				compressed = true;
 			}
 		}
 		rc = pstore_mkfile(type, psi->name, id, count, buf,
-				  (size_t)size, time, psi);
+				  compressed, (size_t)size, time, psi);
 		if (unzipped_len < 0) {
 			/* Free buffer other than big oops */
 			kfree(buf);

^ permalink raw reply related

* Re: [PATCH 1/2] powerpc/85xx: add hardware automatically enter altivec idle state
From: Scott Wood @ 2013-08-16 16:50 UTC (permalink / raw)
  To: Kumar Gala; +Cc: linuxppc-dev, Dongsheng Wang
In-Reply-To: <83499AB5-DCE7-407D-AEC1-B2493D1D38BE@kernel.crashing.org>

On Fri, 2013-08-16 at 06:02 -0500, Kumar Gala wrote:
> On Aug 16, 2013, at 2:23 AM, Dongsheng Wang wrote:
> 
> > From: Wang Dongsheng <dongsheng.wang@freescale.com>
> > 
> > Each core's AltiVec unit may be placed into a power savings mode
> > by turning off power to the unit. Core hardware will automatically
> > power down the AltiVec unit after no AltiVec instructions have
> > executed in N cycles. The AltiVec power-control is triggered by hardware.
> > 
> > Signed-off-by: Wang Dongsheng <dongsheng.wang@freescale.com>
> 
> Why treat this as a idle HW governor vs just some one time setup at boot of the time delay?

It is being done as one-time setup, despite the function name.

Maybe it should be moved into __setup/restore_cpu_e6500 (BTW, we really
should refactor those to reduce duplication) with the timebase bit
number hardcoded rather than a time in us.

As for the PVR check, the upstream kernel doesn't need to care about
rev1, so knowing it's an e6500 is good enough.

-Scott

^ permalink raw reply

* [RFC PATCH v2 0/4] DT: move of_get_cpu_node from PPC to DT core
From: Sudeep KarkadaNagesha @ 2013-08-16 17:39 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, linux-pm, devicetree,
	linuxppc-dev
  Cc: Jonas Bonn, Michal Simek, Sudeep KarkadaNagesha, Rob Herring,
	Rafael J. Wysocki, Grant Likely
In-Reply-To: <1376586580-5409-1-git-send-email-Sudeep.KarkadaNagesha@arm.com>

From: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>

Hi,

This series needs to be prepended with the original series[1][2][3]
Except the first patch in the original series(which is merged into this
as last patch), there is no other change(apart from function signature)
I am posting only this part for feedback separately for review as the
original series is already reviewed and acked.

v1->v2:
- Fixed an issue - updating cell pointer
- Documentated in arch_match_cpu_phys_id that phys_id are values read
  from the device tree only in the weak definition

[Since first 2 patches have not changed, microblaze and openrisc lists are
bouncing for non-members, I am not resending them again. Michal(for
microblaze) has acked when it was first pointed. I am waiting for response
from Jonas(for openrisc)]

Rob, Ben,

If you are OK with these patches and ACK, I can combine and post the comple=
te
series.

Regards,
Sudeep

Sudeep KarkadaNagesha (4):
  microblaze: remove undefined of_get_cpu_node declaration
  openrisc: remove undefined of_get_cpu_node declaration
  powerpc: refactor of_get_cpu_node to support other architectures
  of: move of_get_cpu_node implementation to DT core library

 arch/microblaze/include/asm/prom.h |  3 --
 arch/openrisc/include/asm/prom.h   |  3 --
 arch/powerpc/include/asm/prom.h    |  3 --
 arch/powerpc/kernel/prom.c         | 43 +----------------
 drivers/of/base.c                  | 96 ++++++++++++++++++++++++++++++++++=
++++
 include/linux/cpu.h                |  1 +
 include/linux/of.h                 |  7 +++
 7 files changed, 106 insertions(+), 50 deletions(-)

--=20
1.8.1.2

^ permalink raw reply

* [RFC PATCH v2 3/4] powerpc: refactor of_get_cpu_node to support other architectures
From: Sudeep KarkadaNagesha @ 2013-08-16 17:39 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, linux-pm, devicetree,
	linuxppc-dev
  Cc: Jonas Bonn, Michal Simek, Sudeep KarkadaNagesha, Rob Herring,
	Rafael J. Wysocki, Grant Likely
In-Reply-To: <1376674791-28244-1-git-send-email-Sudeep.KarkadaNagesha@arm.com>

From: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>

Currently different drivers requiring to access cpu device node are
parsing the device tree themselves. Since the ordering in the DT need
not match the logical cpu ordering, the parsing logic needs to consider
that. However, this has resulted in lots of code duplication and in some
cases even incorrect logic.

It's better to consolidate them by adding support for getting cpu
device node for a given logical cpu index in DT core library. However
logical to physical index mapping can be architecture specific.

PowerPC has it's own implementation to get the cpu node for a given
logical index.

This patch refactors the current implementation of of_get_cpu_node.
This in preparation to move the implementation to DT core library.
It separates out the logical to physical mapping so that a default
matching of the physical id to the logical cpu index can be added
when moved to common code. Architecture specific code can override it.

Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
---
 arch/powerpc/kernel/prom.c | 76 ++++++++++++++++++++++++++++--------------=
----
 1 file changed, 47 insertions(+), 29 deletions(-)

diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index eb23ac9..fb12be6 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -865,45 +865,63 @@ static int __init prom_reconfig_setup(void)
 __initcall(prom_reconfig_setup);
 #endif
=20
+bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
+{
+=09return (int)phys_id =3D=3D get_hard_smp_processor_id(cpu);
+}
+
+static bool __of_find_n_match_cpu_property(struct device_node *cpun,
+=09=09=09const char *prop_name, int cpu, unsigned int *thread)
+{
+=09const __be32 *cell;
+=09int ac, prop_len, tid;
+=09u64 hwid;
+
+=09ac =3D of_n_addr_cells(cpun);
+=09cell =3D of_get_property(cpun, prop_name, &prop_len);
+=09if (!cell)
+=09=09return false;
+=09prop_len /=3D sizeof(*cell);
+=09for (tid =3D 0; tid < prop_len; tid++) {
+=09=09hwid =3D of_read_number(cell, ac);
+=09=09if (arch_match_cpu_phys_id(cpu, hwid)) {
+=09=09=09if (thread)
+=09=09=09=09*thread =3D tid;
+=09=09=09return true;
+=09=09}
+=09=09cell +=3D ac;
+=09}
+=09return false;
+}
+
 /* Find the device node for a given logical cpu number, also returns the c=
pu
  * local thread number (index in ibm,interrupt-server#s) if relevant and
  * asked for (non NULL)
  */
 struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
 {
-=09int hardid;
-=09struct device_node *np;
+=09struct device_node *cpun, *cpus;
=20
-=09hardid =3D get_hard_smp_processor_id(cpu);
+=09cpus =3D of_find_node_by_path("/cpus");
+=09if (!cpus) {
+=09=09pr_warn("Missing cpus node, bailing out\n");
+=09=09return NULL;
+=09}
=20
-=09for_each_node_by_type(np, "cpu") {
-=09=09const u32 *intserv;
-=09=09unsigned int plen, t;
+=09for_each_child_of_node(cpus, cpun) {
+=09=09if (of_node_cmp(cpun->type, "cpu"))
+=09=09=09continue;
=20
-=09=09/* Check for ibm,ppc-interrupt-server#s. If it doesn't exist
-=09=09 * fallback to "reg" property and assume no threads
+=09=09/* Check for historical "ibm,ppc-interrupt-server#s" property
+=09=09 * for thread ids on PowerPC. If it doesn't exist fallback to
+=09=09 * standard "reg" property.
 =09=09 */
-=09=09intserv =3D of_get_property(np, "ibm,ppc-interrupt-server#s",
-=09=09=09=09&plen);
-=09=09if (intserv =3D=3D NULL) {
-=09=09=09const u32 *reg =3D of_get_property(np, "reg", NULL);
-=09=09=09if (reg =3D=3D NULL)
-=09=09=09=09continue;
-=09=09=09if (*reg =3D=3D hardid) {
-=09=09=09=09if (thread)
-=09=09=09=09=09*thread =3D 0;
-=09=09=09=09return np;
-=09=09=09}
-=09=09} else {
-=09=09=09plen /=3D sizeof(u32);
-=09=09=09for (t =3D 0; t < plen; t++) {
-=09=09=09=09if (hardid =3D=3D intserv[t]) {
-=09=09=09=09=09if (thread)
-=09=09=09=09=09=09*thread =3D t;
-=09=09=09=09=09return np;
-=09=09=09=09}
-=09=09=09}
-=09=09}
+=09=09if (__of_find_n_match_cpu_property(cpun,
+=09=09=09=09"ibm,ppc-interrupt-server#s", cpu, thread))
+=09=09=09return cpun;
+
+=09=09if (__of_find_n_match_cpu_property(cpun, "reg", cpu, thread))
+=09=09=09return cpun;
 =09}
 =09return NULL;
 }
--=20
1.8.1.2

^ permalink raw reply related

* [RFC PATCH v2 4/4] of: move of_get_cpu_node implementation to DT core library
From: Sudeep KarkadaNagesha @ 2013-08-16 17:39 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, linux-pm, devicetree,
	linuxppc-dev
  Cc: Jonas Bonn, Michal Simek, Sudeep KarkadaNagesha, Rob Herring,
	Rafael J. Wysocki, Grant Likely
In-Reply-To: <1376674791-28244-1-git-send-email-Sudeep.KarkadaNagesha@arm.com>

From: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>

This patch moves the generalized implementation of of_get_cpu_node from
PowerPC to DT core library, thereby adding support for retrieving cpu
node for a given logical cpu index on any architecture.

The CPU subsystem can now use this function to assign of_node in the
cpu device while registering CPUs.

It is recommended to use these helper function only in pre-SMP/early
initialisation stages to retrieve CPU device node pointers in logical
ordering. Once the cpu devices are registered, it can be retrieved easily
from cpu device of_node which avoids unnecessary parsing and matching.

Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
---
 arch/powerpc/include/asm/prom.h |  3 --
 arch/powerpc/kernel/prom.c      | 57 ------------------------
 drivers/of/base.c               | 96 +++++++++++++++++++++++++++++++++++++=
++++
 include/linux/cpu.h             |  1 +
 include/linux/of.h              |  7 +++
 5 files changed, 104 insertions(+), 60 deletions(-)

diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/pro=
m.h
index bc2da15..ac204e0 100644
--- a/arch/powerpc/include/asm/prom.h
+++ b/arch/powerpc/include/asm/prom.h
@@ -43,9 +43,6 @@ void of_parse_dma_window(struct device_node *dn, const vo=
id *dma_window_prop,
=20
 extern void kdump_move_device_tree(void);
=20
-/* CPU OF node matching */
-struct device_node *of_get_cpu_node(int cpu, unsigned int *thread);
-
 /* cache lookup */
 struct device_node *of_find_next_cache_node(struct device_node *np);
=20
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index fb12be6..1c14cd4 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -870,63 +870,6 @@ bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
 =09return (int)phys_id =3D=3D get_hard_smp_processor_id(cpu);
 }
=20
-static bool __of_find_n_match_cpu_property(struct device_node *cpun,
-=09=09=09const char *prop_name, int cpu, unsigned int *thread)
-{
-=09const __be32 *cell;
-=09int ac, prop_len, tid;
-=09u64 hwid;
-
-=09ac =3D of_n_addr_cells(cpun);
-=09cell =3D of_get_property(cpun, prop_name, &prop_len);
-=09if (!cell)
-=09=09return false;
-=09prop_len /=3D sizeof(*cell);
-=09for (tid =3D 0; tid < prop_len; tid++) {
-=09=09hwid =3D of_read_number(cell, ac);
-=09=09if (arch_match_cpu_phys_id(cpu, hwid)) {
-=09=09=09if (thread)
-=09=09=09=09*thread =3D tid;
-=09=09=09return true;
-=09=09}
-=09=09cell +=3D ac;
-=09}
-=09return false;
-}
-
-/* Find the device node for a given logical cpu number, also returns the c=
pu
- * local thread number (index in ibm,interrupt-server#s) if relevant and
- * asked for (non NULL)
- */
-struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
-{
-=09struct device_node *cpun, *cpus;
-
-=09cpus =3D of_find_node_by_path("/cpus");
-=09if (!cpus) {
-=09=09pr_warn("Missing cpus node, bailing out\n");
-=09=09return NULL;
-=09}
-
-=09for_each_child_of_node(cpus, cpun) {
-=09=09if (of_node_cmp(cpun->type, "cpu"))
-=09=09=09continue;
-
-=09=09/* Check for historical "ibm,ppc-interrupt-server#s" property
-=09=09 * for thread ids on PowerPC. If it doesn't exist fallback to
-=09=09 * standard "reg" property.
-=09=09 */
-=09=09if (__of_find_n_match_cpu_property(cpun,
-=09=09=09=09"ibm,ppc-interrupt-server#s", cpu, thread))
-=09=09=09return cpun;
-
-=09=09if (__of_find_n_match_cpu_property(cpun, "reg", cpu, thread))
-=09=09=09return cpun;
-=09}
-=09return NULL;
-}
-EXPORT_SYMBOL(of_get_cpu_node);
-
 #if defined(CONFIG_DEBUG_FS) && defined(DEBUG)
 static struct debugfs_blob_wrapper flat_dt_blob;
=20
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 5c54279..6feb823 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -18,6 +18,7 @@
  *      2 of the License, or (at your option) any later version.
  */
 #include <linux/ctype.h>
+#include <linux/cpu.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/spinlock.h>
@@ -230,6 +231,101 @@ const void *of_get_property(const struct device_node =
*np, const char *name,
 }
 EXPORT_SYMBOL(of_get_property);
=20
+/*
+ * arch_match_cpu_phys_id - Match the given logical CPU and physical id
+ *
+ * @cpu: logical cpu index of a core/thread
+ * @phys_id: physical identifier of a core/thread
+ *
+ * CPU logical to physical index mapping is architecture specific.
+ * However this __weak function provides a default match of physical
+ * id to logical cpu index. phys_id provided here is usually values read
+ * from the device tree which must match the hardware internal registers.
+ *
+ * Returns true if the physical identifier and the logical cpu index
+ * correspond to the same core/thread, false otherwise.
+ */
+bool __weak arch_match_cpu_phys_id(int cpu, u64 phys_id)
+{
+=09return (u32)phys_id =3D=3D cpu;
+}
+
+/**
+ * Checks if the given "prop_name" property holds the physical id of the
+ * core/thread corresponding to the logical cpu 'cpu'. If 'thread' is not
+ * NULL, local thread number within the core is returned in it.
+ */
+static bool __of_find_n_match_cpu_property(struct device_node *cpun,
+=09=09=09const char *prop_name, int cpu, unsigned int *thread)
+{
+=09const __be32 *cell;
+=09int ac, prop_len, tid;
+=09u64 hwid;
+
+=09ac =3D of_n_addr_cells(cpun);
+=09cell =3D of_get_property(cpun, prop_name, &prop_len);
+=09if (!cell)
+=09=09return false;
+=09prop_len /=3D sizeof(*cell);
+=09for (tid =3D 0; tid < prop_len; tid++) {
+=09=09hwid =3D of_read_number(cell, ac);
+=09=09if (arch_match_cpu_phys_id(cpu, hwid)) {
+=09=09=09if (thread)
+=09=09=09=09*thread =3D tid;
+=09=09=09return true;
+=09=09}
+=09=09cell +=3D ac;
+=09}
+=09return false;
+}
+
+/**
+ * of_get_cpu_node - Get device node associated with the given logical CPU
+ *
+ * @cpu: CPU number(logical index) for which device node is required
+ * @thread: if not NULL, local thread number within the physical core is
+ *          returned
+ *
+ * The main purpose of this function is to retrieve the device node for th=
e
+ * given logical CPU index. It should be used to initialize the of_node in
+ * cpu device. Once of_node in cpu device is populated, all the further
+ * references can use that instead.
+ *
+ * CPU logical to physical index mapping is architecture specific and is b=
uilt
+ * before booting secondary cores. This function uses arch_match_cpu_phys_=
id
+ * which can be overridden by architecture specific implementation.
+ *
+ * Returns a node pointer for the logical cpu if found, else NULL.
+ */
+struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
+{
+=09struct device_node *cpun, *cpus;
+
+=09cpus =3D of_find_node_by_path("/cpus");
+=09if (!cpus) {
+=09=09pr_warn("Missing cpus node, bailing out\n");
+=09=09return NULL;
+=09}
+
+=09for_each_child_of_node(cpus, cpun) {
+=09=09if (of_node_cmp(cpun->type, "cpu"))
+=09=09=09continue;
+#ifdef CONFIG_PPC
+=09=09/* Check for historical "ibm,ppc-interrupt-server#s" property
+=09=09 * for thread ids on PowerPC. If it doesn't exist fallback to
+=09=09 * standard "reg" property.
+=09=09 */
+=09=09if (__of_find_n_match_cpu_property(cpun,
+=09=09=09=09"ibm,ppc-interrupt-server#s", cpu, thread))
+=09=09=09return cpun;
+#endif
+=09=09if (__of_find_n_match_cpu_property(cpun, "reg", cpu, thread))
+=09=09=09return cpun;
+=09}
+=09return NULL;
+}
+EXPORT_SYMBOL(of_get_cpu_node);
+
 /** Checks if the given "compat" string matches one of the strings in
  * the device's "compatible" property
  */
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index ab0eade..3dfed2b 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -28,6 +28,7 @@ struct cpu {
 extern int register_cpu(struct cpu *cpu, int num);
 extern struct device *get_cpu_device(unsigned cpu);
 extern bool cpu_is_hotpluggable(unsigned cpu);
+extern bool arch_match_cpu_phys_id(int cpu, u64 phys_id);
=20
 extern int cpu_add_dev_attr(struct device_attribute *attr);
 extern void cpu_remove_dev_attr(struct device_attribute *attr);
diff --git a/include/linux/of.h b/include/linux/of.h
index 1fd08ca..c0bb2f1 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -266,6 +266,7 @@ extern int of_device_is_available(const struct device_n=
ode *device);
 extern const void *of_get_property(const struct device_node *node,
 =09=09=09=09const char *name,
 =09=09=09=09int *lenp);
+extern struct device_node *of_get_cpu_node(int cpu, unsigned int *thread);
 #define for_each_property_of_node(dn, pp) \
 =09for (pp =3D dn->properties; pp !=3D NULL; pp =3D pp->next)
=20
@@ -459,6 +460,12 @@ static inline const void *of_get_property(const struct=
 device_node *node,
 =09return NULL;
 }
=20
+static inline struct device_node *of_get_cpu_node(int cpu,
+=09=09=09=09=09unsigned int *thread)
+{
+=09return NULL;
+}
+
 static inline int of_property_read_u64(const struct device_node *np,
 =09=09=09=09       const char *propname, u64 *out_value)
 {
--=20
1.8.1.2

^ permalink raw reply related

* RE: [PATCH v6 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Bhushan Bharat-R65777 @ 2013-08-16 18:24 UTC (permalink / raw)
  To: Chen Guangyu-B42378, broonie@kernel.org, lars@metafoo.de,
	p.zabel@pengutronix.de, s.hauer@pengutronix.de
  Cc: mark.rutland@arm.com, devicetree@vger.kernel.org,
	alsa-devel@alsa-project.org, swarren@wwwdotorg.org,
	linuxppc-dev@lists.ozlabs.org, timur@tabi.org,
	rob.herring@calxeda.com, tomasz.figa@gmail.com,
	shawn.guo@linaro.org, festevam@gmail.com
In-Reply-To: <df3c09cd5679649b144bb45dfd5fc746628266d4.1376657643.git.b42378@freescale.com>



> -----Original Message-----
> From: Linuxppc-dev [mailto:linuxppc-dev-
> bounces+bharat.bhushan=3Dfreescale.com@lists.ozlabs.org] On Behalf Of Nic=
olin Chen
> Sent: Friday, August 16, 2013 6:27 PM
> To: broonie@kernel.org; lars@metafoo.de; p.zabel@pengutronix.de;
> s.hauer@pengutronix.de
> Cc: mark.rutland@arm.com; devicetree@vger.kernel.org; alsa-devel@alsa-
> project.org; swarren@wwwdotorg.org; festevam@gmail.com; timur@tabi.org;
> rob.herring@calxeda.com; tomasz.figa@gmail.com; shawn.guo@linaro.org; lin=
uxppc-
> dev@lists.ozlabs.org
> Subject: [PATCH v6 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
>=20
> This patch add S/PDIF controller driver for Freescale SoC.

Please give some more description of the driver?

>=20
> Signed-off-by: Nicolin Chen <b42378@freescale.com>
> ---
>  .../devicetree/bindings/sound/fsl,spdif.txt        |   56 +
>  sound/soc/fsl/Kconfig                              |    3 +
>  sound/soc/fsl/Makefile                             |    2 +
>  sound/soc/fsl/fsl_spdif.c                          | 1272 ++++++++++++++=
++++++
>  sound/soc/fsl/fsl_spdif.h                          |  224 ++++
>  5 files changed, 1557 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
>=20
> diff --git a/Documentation/devicetree/bindings/sound/fsl,spdif.txt
> b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
> new file mode 100644
> index 0000000..5549ce3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
> @@ -0,0 +1,56 @@
> +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".
> +
> +  - 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		comments
> +	"core"		The core clock of spdif controller
> +	"rxtx<0-7>"	Clock source list for tx and rx clock.
> +			This clock list should be identical to
> +			the source list connecting to the spdif
> +			clock mux in "SPDIF Transceiver Clock
> +			Diagram" of SoC reference manual. It
> +			can also be referred to TxClk_Source
> +			bit of register SPDIF_STC.
> +
> +Example:
> +
> +spdif: spdif@02004000 {
> +	compatible =3D "fsl,imx6q-spdif",
> +		"fsl,imx35-spdif";
> +	reg =3D <0x02004000 0x4000>;
> +	interrupts =3D <0 52 0x04>;
> +	dmas =3D <&sdma 14 18 0>,
> +	       <&sdma 15 18 0>;
> +	dma-names =3D "rx", "tx";
> +
> +	clocks =3D <&clks 197>, <&clks 3>,
> +	       <&clks 197>, <&clks 107>,
> +	       <&clks 0>, <&clks 118>,
> +	       <&clks 62>, <&clks 139>,
> +	       <&clks 0>;
> +	clock-names =3D "core", "rxtx0",
> +		"rxtx1", "rxtx2",
> +		"rxtx3", "rxtx4",
> +		"rxtx5", "rxtx6",
> +		"rxtx7";
> +
> +	status =3D "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
>=20
> +config SND_SOC_FSL_SPDIF
> +	tristate
> +
>  config SND_SOC_FSL_UTILS
>  	tristate
>=20
> 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) +=3D snd-soc-p1022-rdk=
.o
>=20
>  # Freescale PowerPC SSI/DMA Platform Support
>  snd-soc-fsl-ssi-objs :=3D fsl_ssi.o
> +snd-soc-fsl-spdif-objs :=3D fsl_spdif.o
>  snd-soc-fsl-utils-objs :=3D fsl_utils.o
>  snd-soc-fsl-dma-objs :=3D fsl_dma.o
>  obj-$(CONFIG_SND_SOC_FSL_SSI) +=3D snd-soc-fsl-ssi.o
> +obj-$(CONFIG_SND_SOC_FSL_SPDIF) +=3D snd-soc-fsl-spdif.o
>  obj-$(CONFIG_SND_SOC_FSL_UTILS) +=3D snd-soc-fsl-utils.o
>  obj-$(CONFIG_SND_SOC_POWERPC_DMA) +=3D snd-soc-fsl-dma.o
>=20
> diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
> new file mode 100644
> index 0000000..e00125e
> --- /dev/null
> +++ b/sound/soc/fsl/fsl_spdif.c
> @@ -0,0 +1,1272 @@
> +/*
> + * 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 Licen=
se
> + * 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)
> +
> +/* Index list for the values that has if (DPLL Locked) condition */
> +static u8 srpc_dpll_locked[] =3D { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb, };
										   ^
                                                                     ^ un-n=
ecessary comma.

> +#define SRPC_NODPLL_START1	0x5
> +#define SRPC_NODPLL_START2	0xc
> +
> +/*
> + * 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;

They does not look like pointer?

> +
> +	/* 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;
> +	u8 txclk_div[SPDIF_TXRATE_MAX];
> +	u8 txclk_src[SPDIF_TXRATE_MAX];
> +	u8 rxclk_src;
> +	struct clk *txclk[SPDIF_TXRATE_MAX];
> +	struct clk *rxclk;
> +	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 =3D spdif_priv->regmap;
> +	struct platform_device *pdev =3D spdif_priv->pdev;
> +	u32 val, i;
> +	int ret;
> +
> +	/* Valid address set of SPDIF is {[0x0-0x38], 0x44, 0x50} */
> +	for (i =3D 0 ; i <=3D REG_SPDIF_STC; i +=3D 4) {
> +		ret =3D regmap_read(regmap, REG_SPDIF_SCR + i, &val);
> +		if (!ret)
> +			dev_dbg(&pdev->dev, "REG 0x%02x =3D 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 =3D spdif_priv->regmap;
> +	struct platform_device *pdev =3D spdif_priv->pdev;
> +	u32 locked;
> +
> +	regmap_read(regmap, REG_SPDIF_SRPC, &locked);
> +	locked &=3D 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 =3D spdif_priv->regmap;
> +	struct platform_device *pdev =3D 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 =3D &spdif_priv->fsl_spdif_control;
> +	struct regmap *regmap =3D spdif_priv->regmap;
> +	struct platform_device *pdev =3D spdif_priv->pdev;
> +	u32 *pos, size, val, reg;
> +
> +	switch (name) {
> +	case 'U':
> +		pos =3D &ctrl->upos;
> +		size =3D SPDIF_UBITS_SIZE;
> +		reg =3D REG_SPDIF_SRU;
> +		break;
> +	case 'Q':
> +		pos =3D &ctrl->qpos;
> +		size =3D SPDIF_QSUB_SIZE;
> +		reg =3D REG_SPDIF_SRQ;
> +		break;
> +	default:
> +		return;

Should return error.

> +	}
> +
> +	dev_dbg(&pdev->dev, "isr: %c Channel receive register full\n", name);
> +
> +	if (*pos >=3D size * 2) {
> +		*pos =3D 0;
> +	} else if (unlikely((*pos % size) + 3 > size)) {
> +		dev_err(&pdev->dev, "User bit receivce buffer overflow\n");
> +		return;

Should return error.

> +	}
> +
> +	regmap_read(regmap, reg, &val);
> +	ctrl->subcode[*pos++] =3D val >> 16;
> +	ctrl->subcode[*pos++] =3D val >> 8;
> +	ctrl->subcode[*pos++] =3D val;
> +}
> +
> +/* U/Q Channel sync found */
> +static void spdif_irq_uq_sync(struct fsl_spdif_priv *spdif_priv)
> +{
> +	struct spdif_mixer_control *ctrl =3D &spdif_priv->fsl_spdif_control;
> +	struct platform_device *pdev =3D spdif_priv->pdev;
> +
> +	dev_dbg(&pdev->dev, "isr: U/Q Channel sync found\n");
> +
> +	/* U/Q buffer reset */
> +	if (ctrl->qpos =3D=3D 0)
> +		return;
> +
> +	/* set ready to this buffer */
> +	ctrl->ready_buf =3D (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 =3D &spdif_priv->fsl_spdif_control;
> +	struct regmap *regmap =3D spdif_priv->regmap;
> +	struct platform_device *pdev =3D 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);

Above prints says read u/q data and buffer reset, what is buffer reset? Is =
that read on clear?

> +
> +	/* drop this U/Q buffer */
> +	ctrl->ready_buf =3D 0;
> +	ctrl->upos =3D 0;
> +	ctrl->qpos =3D 0;
> +}
> +
> +/* Get spdif interrupt status and clear the interrupt */
> +static u32 spdif_intr_status_clear(struct fsl_spdif_priv *spdif_priv)
> +{
> +	struct regmap *regmap =3D 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 =3D (struct fsl_spdif_priv *)devid;
> +	struct platform_device *pdev =3D spdif_priv->pdev;
> +	u32 sis;
> +
> +	sis =3D 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 =3D spdif_priv->regmap;
> +	u32 val, cycle =3D 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--);

What if reset is not cleared and timeout happen?

> +}
> +
> +static void spdif_set_cstatus(struct spdif_mixer_control *ctrl,
> +				u8 mask, u8 cstatus)
> +{
> +	ctrl->ch_status[3] &=3D ~mask;
> +	ctrl->ch_status[3] |=3D cstatus & mask;
> +}
> +
> +static u8 reverse_bits(u8 input)
> +{
> +	u8 tmp =3D input;
> +
> +	tmp =3D ((tmp & 0b10101010) >> 1) | ((tmp << 1) & 0b10101010);
> +	tmp =3D ((tmp & 0b11001100) >> 2) | ((tmp << 2) & 0b11001100);
> +	tmp =3D ((tmp & 0b11110000) >> 4) | ((tmp << 4) & 0b11110000);

What is this logic, can the hardcoding be removed and some description on a=
bove calculation?


> +
> +	return tmp;
> +}
> +
> +static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv=
)
> +{
> +	struct spdif_mixer_control *ctrl =3D &spdif_priv->fsl_spdif_control;
> +	struct regmap *regmap =3D spdif_priv->regmap;
> +	struct platform_device *pdev =3D spdif_priv->pdev;
> +	u32 ch_status;
> +
> +	ch_status =3D (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 =3D 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 =3D spdif_priv->rxclk_src;
> +	struct regmap *regmap =3D spdif_priv->regmap;
> +
> +	if (clksrc >=3D SRPC_CLKSRC_MAX || gainsel >=3D GAINSEL_MULTI_MAX)
> +		return -EINVAL;
> +
> +	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 =3D 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 =3D substream->private_data;
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(rtd->cpu_=
dai);
> +	struct spdif_mixer_control *ctrl =3D &spdif_priv->fsl_spdif_control;
> +	struct regmap *regmap =3D spdif_priv->regmap;
> +	struct platform_device *pdev =3D spdif_priv->pdev;
> +	unsigned long csfs =3D 0;
> +	u8 clk =3D -1, div =3D 1;

Not signed type?

> +	u32 stc, mask, rate;
> +
> +	switch (sample_rate) {
> +	case 32000:
> +		rate =3D SPDIF_TXRATE_32000;
> +		csfs =3D IEC958_AES3_CON_FS_32000;
> +		break;
> +	case 44100:
> +		rate =3D SPDIF_TXRATE_44100;
> +		csfs =3D IEC958_AES3_CON_FS_44100;
> +		break;
> +	case 48000:
> +		rate =3D SPDIF_TXRATE_48000;
> +		csfs =3D IEC958_AES3_CON_FS_48000;
> +		break;
> +	default:
> +		dev_err(&pdev->dev, "unsupported samplerate %d\n", sample_rate);
> +		return -EINVAL;
> +	}
> +
> +	clk =3D spdif_priv->txclk_src[rate];
> +	div =3D spdif_priv->txclk_div[rate];
> +
> +	/*
> +	 * 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 =3D %d\n",
> +			(int)(64 * sample_rate * div));
> +	dev_dbg(&pdev->dev, "acutal clock rate =3D %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 =3D STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div);
> +	mask =3D STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MAS=
K;
> +	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 =3D substream->private_data;
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(rtd->cpu_=
dai);
> +	struct regmap *regmap =3D 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 =3D=3D SNDRV_PCM_STREAM_PLAYBACK) {
> +		scr =3D SCR_TXFIFO_AUTOSYNC | SCR_TXFIFO_CTRL_NORMAL |
> +			SCR_TXSEL_NORMAL | SCR_USRC_SEL_CHIP |
> +			SCR_TXFIFO_FSEL_IF8;
> +		mask =3D SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
> +			SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
> +			SCR_TXFIFO_FSEL_MASK;
> +		for (i =3D 0; i < SPDIF_TXRATE_MAX; i++)
> +			clk_enable(spdif_priv->txclk[i]);
> +	} else {
> +		scr =3D SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC;
> +		mask =3D 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 =3D substream->private_data;
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(rtd->cpu_=
dai);
> +	struct regmap *regmap =3D spdif_priv->regmap;
> +	u32 scr, mask, i;
> +
> +	if (substream->stream =3D=3D SNDRV_PCM_STREAM_PLAYBACK) {
> +		scr =3D 0;
> +		mask =3D SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
> +			SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
> +			SCR_TXFIFO_FSEL_MASK;
> +		for (i =3D 0; i < SPDIF_TXRATE_MAX; i++)
> +			clk_disable(spdif_priv->txclk[i]);
> +	} else {
> +		scr =3D SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO;
> +		mask =3D 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 =3D substream->private_data;
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(rtd->cpu_=
dai);
> +	struct spdif_mixer_control *ctrl =3D &spdif_priv->fsl_spdif_control;
> +	struct platform_device *pdev =3D spdif_priv->pdev;
> +	u32 sample_rate =3D params_rate(params);
> +	int ret =3D 0;
> +
> +	if (substream->stream =3D=3D SNDRV_PCM_STREAM_PLAYBACK) {
> +		ret  =3D 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 =3D 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 =3D substream->private_data;
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(rtd->cpu_=
dai);
> +	struct regmap *regmap =3D spdif_priv->regmap;
> +	int is_playack =3D (substream->stream =3D=3D SNDRV_PCM_STREAM_PLAYBACK)=
;
> +	u32 intr =3D is_playack ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE;
> +	u32 dmaen =3D 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 =3D {
> +	.startup =3D fsl_spdif_startup,
> +	.hw_params =3D fsl_spdif_hw_params,
> +	.trigger =3D fsl_spdif_trigger,
> +	.shutdown =3D fsl_spdif_shutdown,
> +};
> +
> +
> +/*
> + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> + * 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
> + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> + */
> +
> +static int fsl_spdif_info(struct snd_kcontrol *kcontrol,
> +				struct snd_ctl_elem_info *uinfo)
> +{
> +	uinfo->type =3D SNDRV_CTL_ELEM_TYPE_IEC958;
> +	uinfo->count =3D 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 =3D snd_kcontrol_chip(kcontrol);
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(cpu_dai);
> +	struct spdif_mixer_control *ctrl =3D &spdif_priv->fsl_spdif_control;
> +
> +	uvalue->value.iec958.status[0] =3D ctrl->ch_status[0];
> +	uvalue->value.iec958.status[1] =3D ctrl->ch_status[1];
> +	uvalue->value.iec958.status[2] =3D ctrl->ch_status[2];
> +	uvalue->value.iec958.status[3] =3D 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 =3D snd_kcontrol_chip(kcontrol);
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(cpu_dai);
> +	struct spdif_mixer_control *ctrl =3D &spdif_priv->fsl_spdif_control;
> +
> +	ctrl->ch_status[0] =3D uvalue->value.iec958.status[0];
> +	ctrl->ch_status[1] =3D uvalue->value.iec958.status[1];
> +	ctrl->ch_status[2] =3D uvalue->value.iec958.status[2];
> +	ctrl->ch_status[3] =3D 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 =3D snd_kcontrol_chip(kcontrol);
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(cpu_dai);
> +	struct regmap *regmap =3D 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] =3D (cstatus >> 16) & 0xFF;
> +	ucontrol->value.iec958.status[1] =3D (cstatus >> 8) & 0xFF;
> +	ucontrol->value.iec958.status[2] =3D cstatus & 0xFF;
> +
> +	regmap_read(regmap, REG_SPDIF_SRCSL, &cstatus);
> +	ucontrol->value.iec958.status[3] =3D (cstatus >> 16) & 0xFF;
> +	ucontrol->value.iec958.status[4] =3D (cstatus >> 8) & 0xFF;
> +	ucontrol->value.iec958.status[5] =3D 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 =3D snd_kcontrol_chip(kcontrol);
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(cpu_dai);
> +	struct spdif_mixer_control *ctrl =3D &spdif_priv->fsl_spdif_control;
> +	unsigned long flags;
> +	int ret =3D 0;
> +
> +	spin_lock_irqsave(&ctrl->ctl_lock, flags);
> +	if (ctrl->ready_buf) {
> +		int idx =3D (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE;
> +		memcpy(&ucontrol->value.iec958.subcode[0],
> +				&ctrl->subcode[idx], SPDIF_UBITS_SIZE);
> +	} else {
> +		ret =3D -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 =3D SNDRV_CTL_ELEM_TYPE_BYTES;
> +	uinfo->count =3D 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 =3D snd_kcontrol_chip(kcontrol);
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(cpu_dai);
> +	struct spdif_mixer_control *ctrl =3D &spdif_priv->fsl_spdif_control;
> +	unsigned long flags;
> +	int ret =3D 0;
> +
> +	spin_lock_irqsave(&ctrl->ctl_lock, flags);
> +	if (ctrl->ready_buf) {
> +		int idx =3D (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE;
> +		memcpy(&ucontrol->value.bytes.data[0],
> +				&ctrl->qsub[idx], SPDIF_QSUB_SIZE);
> +	} else {
> +		ret =3D -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 =3D SNDRV_CTL_ELEM_TYPE_BOOLEAN;
> +	uinfo->count =3D 1;
> +	uinfo->value.integer.min =3D 0;
> +	uinfo->value.integer.max =3D 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 =3D snd_kcontrol_chip(kcontrol);
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(cpu_dai);
> +	struct regmap *regmap =3D spdif_priv->regmap;
> +	u32 val;
> +
> +	val =3D regmap_read(regmap, REG_SPDIF_SIS, &val);
> +	ucontrol->value.integer.value[0] =3D (val & INT_VAL_NOGOOD) !=3D 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 =3D SNDRV_CTL_ELEM_TYPE_INTEGER;
> +	uinfo->count =3D 1;
> +	uinfo->value.integer.min =3D 16000;
> +	uinfo->value.integer.max =3D 96000;
> +
> +	return 0;
> +}
> +
> +static u32 gainsel_multi[GAINSEL_MULTI_MAX] =3D {
> +	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 =3D spdif_priv->regmap;
> +	struct platform_device *pdev =3D spdif_priv->pdev;
> +	u64 tmpval64, busclk_freq =3D 0;
> +	u32 freqmeas, phaseconf;
> +	enum spdif_rxclk_src clksrc;
> +
> +	regmap_read(regmap, REG_SPDIF_SRFM, &freqmeas);
> +	regmap_read(regmap, REG_SPDIF_SRPC, &phaseconf);
> +
> +	clksrc =3D (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
> +	if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) {
> +		/* get bus clock from system */
> +		busclk_freq =3D clk_get_rate(spdif_priv->rxclk);
> +	}
> +
> +	/* FreqMeas_CLK =3D (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
> +	tmpval64 =3D (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 =3D snd_kcontrol_chip(kcontrol);
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(cpu_dai);
> +	int rate =3D spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL);
> +
> +	if (atomic_read(&spdif_priv->dpll_locked))
> +		ucontrol->value.integer.value[0] =3D rate;
> +	else
> +		ucontrol->value.integer.value[0] =3D 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 =3D SNDRV_CTL_ELEM_TYPE_BOOLEAN;
> +	uinfo->count =3D 1;
> +	uinfo->value.integer.min =3D 0;
> +	uinfo->value.integer.max =3D 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 =3D snd_kcontrol_chip(kcontrol);
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(cpu_dai);
> +	struct regmap *regmap =3D spdif_priv->regmap;
> +	u32 val;
> +
> +	regmap_read(regmap, REG_SPDIF_SRCD, &val);
> +	ucontrol->value.integer.value[0] =3D (val & SRCD_CD_USER) !=3D 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 =3D snd_kcontrol_chip(kcontrol);
> +	struct fsl_spdif_priv *spdif_priv =3D snd_soc_dai_get_drvdata(cpu_dai);
> +	struct regmap *regmap =3D spdif_priv->regmap;
> +	u32 val =3D 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[] =3D {
> +	/* status cchanel controller */
> +	{
> +		.iface =3D SNDRV_CTL_ELEM_IFACE_MIXER,
> +		.name =3D SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
> +		.access =3D SNDRV_CTL_ELEM_ACCESS_READ |
> +			SNDRV_CTL_ELEM_ACCESS_WRITE |
> +			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
> +		.info =3D fsl_spdif_info,
> +		.get =3D fsl_spdif_pb_get,
> +		.put =3D fsl_spdif_pb_put,
> +	},
> +	{
> +		.iface =3D SNDRV_CTL_ELEM_IFACE_PCM,
> +		.name =3D SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
> +		.access =3D SNDRV_CTL_ELEM_ACCESS_READ |
> +			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
> +		.info =3D fsl_spdif_info,
> +		.get =3D fsl_spdif_capture_get,
> +	},
> +	/* user bits controller */
> +	{
> +		.iface =3D SNDRV_CTL_ELEM_IFACE_PCM,
> +		.name =3D "IEC958 Subcode Capture Default",
> +		.access =3D SNDRV_CTL_ELEM_ACCESS_READ |
> +			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
> +		.info =3D fsl_spdif_info,
> +		.get =3D fsl_spdif_subcode_get,
> +	},
> +	{
> +		.iface =3D SNDRV_CTL_ELEM_IFACE_PCM,
> +		.name =3D "IEC958 Q-subcode Capture Default",
> +		.access =3D SNDRV_CTL_ELEM_ACCESS_READ |
> +			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
> +		.info =3D fsl_spdif_qinfo,
> +		.get =3D fsl_spdif_qget,
> +	},
> +	/* valid bit error controller */
> +	{
> +		.iface =3D SNDRV_CTL_ELEM_IFACE_PCM,
> +		.name =3D "IEC958 V-Bit Errors",
> +		.access =3D SNDRV_CTL_ELEM_ACCESS_READ |
> +			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
> +		.info =3D fsl_spdif_vbit_info,
> +		.get =3D fsl_spdif_vbit_get,
> +	},
> +	/* DPLL lock info get controller */
> +	{
> +		.iface =3D SNDRV_CTL_ELEM_IFACE_PCM,
> +		.name =3D "RX Sample Rate",
> +		.access =3D SNDRV_CTL_ELEM_ACCESS_READ |
> +			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
> +		.info =3D fsl_spdif_rxrate_info,
> +		.get =3D fsl_spdif_rxrate_get,
> +	},
> +	/* User bit sync mode set/get controller */
> +	{
> +		.iface =3D SNDRV_CTL_ELEM_IFACE_PCM,
> +		.name =3D "IEC958 USyncMode CDText",
> +		.access =3D SNDRV_CTL_ELEM_ACCESS_READ |
> +			SNDRV_CTL_ELEM_ACCESS_WRITE |
> +			SNDRV_CTL_ELEM_ACCESS_VOLATILE,
> +		.info =3D fsl_spdif_usync_info,
> +		.get =3D fsl_spdif_usync_get,
> +		.put =3D fsl_spdif_usync_put,
> +	},
> +};
> +
> +static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
> +{
> +	struct fsl_spdif_priv *spdif_private =3D snd_soc_dai_get_drvdata(dai);
> +
> +	dai->playback_dma_data =3D &spdif_private->dma_params_tx;
> +	dai->capture_dma_data =3D &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 =3D {
> +	.probe =3D &fsl_spdif_dai_probe,
> +	.playback =3D {
> +		.channels_min =3D 2,
> +		.channels_max =3D 2,
> +		.rates =3D FSL_SPDIF_RATES_PLAYBACK,
> +		.formats =3D FSL_SPDIF_FORMATS_PLAYBACK,
> +	},
> +	.capture =3D {
> +		.channels_min =3D 2,
> +		.channels_max =3D 2,
> +		.rates =3D FSL_SPDIF_RATES_CAPTURE,
> +		.formats =3D FSL_SPDIF_FORMATS_CAPTURE,
> +	},
> +	.ops =3D &fsl_spdif_dai_ops,
> +};
> +
> +static const struct snd_soc_component_driver fsl_spdif_component =3D {
> +	.name		=3D "fsl-spdif",
> +};
> +
> +/*
> + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> + * FSL SPDIF REGMAP
> + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> + */
> +
> +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 */

Where us sync :) ?

-Bharat

> +	return true;
> +}
> +
> +static const struct regmap_config fsl_spdif_regmap_config =3D {
> +	.reg_bits =3D 32,
> +	.reg_stride =3D 4,
> +	.val_bits =3D 32,
> +
> +	.max_register =3D REG_SPDIF_STC,
> +	.readable_reg =3D fsl_spdif_readable_reg,
> +	.writeable_reg =3D fsl_spdif_writeable_reg,
> +	.volatile_reg =3D fsl_spdif_volatile_reg,
> +	.cache_type =3D REGCACHE_RBTREE,
> +};
> +
> +static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
> +				struct clk *clk, u64 savesub,
> +				enum spdif_txrate index)
> +{
> +	const u32 rate[] =3D { 32000, 44100, 48000, };
> +	u64 rate_ideal, rate_actual, sub;
> +	u32 div, arate;
> +
> +	for (div =3D 1; div <=3D 128; div++) {
> +		rate_ideal =3D rate[index] * (div + 1) * 64;
> +		rate_actual =3D clk_round_rate(clk, rate_ideal);
> +
> +		arate =3D rate_actual / 64;
> +		arate /=3D div;
> +
> +		if (arate =3D=3D rate[index]) {
> +			/* We are lucky */
> +			savesub =3D 0;
> +			spdif_priv->txclk_div[index] =3D div;
> +			break;
> +		} else if (arate / rate[index] =3D=3D 1) {
> +			/* A little bigger than expect */
> +			sub =3D (arate - rate[index]) * 100000;
> +			do_div(sub, rate[index]);
> +			if (sub < savesub) {
> +				savesub =3D sub;
> +				spdif_priv->txclk_div[index] =3D div;
> +			}
> +		} else if (rate[index] / arate =3D=3D 1) {
> +			/* A little smaller than expect */
> +			sub =3D (rate[index] - arate) * 100000;
> +			do_div(sub, rate[index]);
> +			if (sub < savesub) {
> +				savesub =3D sub;
> +				spdif_priv->txclk_div[index] =3D div;
> +			}
> +		}
> +	}
> +
> +	return savesub;
> +}
> +
> +static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
> +				enum spdif_txrate index)
> +{
> +	const u32 rate[] =3D { 32000, 44100, 48000, };
> +	struct platform_device *pdev =3D spdif_priv->pdev;
> +	struct device *dev =3D &pdev->dev;
> +	u64 savesub =3D 100000, ret;
> +	struct clk *clk;
> +	char tmp[16];
> +	int i;
> +
> +	for (i =3D 0; i < STC_TXCLK_SRC_MAX; i++) {
> +		sprintf(tmp, "rxtx%d", i);
> +		clk =3D devm_clk_get(&pdev->dev, tmp);
> +		if (IS_ERR(clk)) {
> +			dev_err(dev, "no rxtx%d property in devicetree\n", i);
> +			return PTR_ERR(clk);
> +		}
> +		if (!clk_get_rate(clk))
> +			continue;
> +
> +		ret =3D fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index);
> +		if (savesub =3D=3D ret)
> +			continue;
> +
> +		savesub =3D ret;
> +		spdif_priv->txclk[index] =3D clk;
> +		spdif_priv->txclk_src[index] =3D i;
> +
> +		/* To quick catch a divisor, we allow a 0.1% deviation */
> +		if (savesub < 100)
> +			break;
> +	}
> +
> +	dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate=
",
> +			spdif_priv->txclk_src[index], rate[index]);
> +	dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate",
> +			spdif_priv->txclk_div[index], rate[index]);
> +
> +	return 0;
> +}
> +
> +static int fsl_spdif_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np =3D pdev->dev.of_node;
> +	struct fsl_spdif_priv *spdif_priv;
> +	struct spdif_mixer_control *ctrl;
> +	struct resource *res;
> +	void __iomem *regs;
> +	int irq, ret, i;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	spdif_priv =3D devm_kzalloc(&pdev->dev,
> +			sizeof(struct fsl_spdif_priv) + strlen(np->name) + 1,
> GFP_KERNEL);
> +	if (!spdif_priv) {
> +		dev_err(&pdev->dev, "could not allocate DAI object\n");
> +		return -ENOMEM;
> +	}
> +
> +	strcpy(spdif_priv->name, np->name);
> +
> +	spdif_priv->pdev =3D 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 =3D spdif_priv->name;
> +
> +	/* Get the addresses and IRQ */
> +	res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (IS_ERR(res)) {
> +		dev_err(&pdev->dev, "could not determine device resources\n");
> +		return PTR_ERR(res);
> +	}
> +
> +	regs =3D devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(regs)) {
> +		dev_err(&pdev->dev, "could not map device resources\n");
> +		return PTR_ERR(regs);
> +	}
> +
> +	spdif_priv->regmap =3D 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");
> +		return PTR_ERR(spdif_priv->regmap);
> +	}
> +
> +	irq =3D platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
> +		return irq;
> +	}
> +
> +	ret =3D devm_request_irq(&pdev->dev, irq, spdif_isr, 0,
> +			spdif_priv->name, spdif_priv);
> +	if (ret) {
> +		dev_err(&pdev->dev, "could not claim irq %u\n", irq);
> +		return ret;
> +	}
> +
> +	/* Select clock source for rx/tx clock */
> +	spdif_priv->rxclk =3D devm_clk_get(&pdev->dev, "rxtx1");
> +	if (IS_ERR(spdif_priv->rxclk)) {
> +		dev_err(&pdev->dev, "no rxtx1 property in devicetree\n");
> +		return PTR_ERR(spdif_priv->rxclk);
> +	}
> +	spdif_priv->rxclk_src =3D DEFAULT_RXCLK_SRC;
> +
> +	for (i =3D 0; i < SPDIF_TXRATE_MAX; i++) {
> +		ret =3D fsl_spdif_probe_txclk(spdif_priv, i);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* Prepare rx/tx clock */
> +	clk_prepare(spdif_priv->rxclk);
> +	for (i =3D 0; i < SPDIF_TXRATE_MAX; i++)
> +		clk_prepare(spdif_priv->txclk[i]);
> +
> +	/* initial spinlock for control data */
> +	ctrl =3D &spdif_priv->fsl_spdif_control;
> +	spin_lock_init(&ctrl->ctl_lock);
> +
> +	/* init tx channel status default value */
> +	ctrl->ch_status[0] =3D
> +		IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_5015;
> +	ctrl->ch_status[1] =3D IEC958_AES1_CON_DIGDIGCONV_ID;
> +	ctrl->ch_status[2] =3D 0x00;
> +	ctrl->ch_status[3] =3D
> +		IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM;
> +
> +	atomic_set(&spdif_priv->dpll_locked, 0);
> +
> +	spdif_priv->dma_params_tx.maxburst =3D FSL_SPDIF_TXFIFO_WML;
> +	spdif_priv->dma_params_rx.maxburst =3D FSL_SPDIF_RXFIFO_WML;
> +	spdif_priv->dma_params_tx.addr =3D res->start + REG_SPDIF_STL;
> +	spdif_priv->dma_params_rx.addr =3D res->start + REG_SPDIF_SRL;
> +
> +	/* Register with ASoC */
> +	dev_set_drvdata(&pdev->dev, spdif_priv);
> +
> +	ret =3D 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 =3D 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);
> +	for (i =3D 0; i < SPDIF_TXRATE_MAX; i++)
> +		clk_unprepare(spdif_priv->txclk[i]);
> +	clk_unprepare(spdif_priv->rxclk);
> +
> +	return ret;
> +}
> +
> +static int fsl_spdif_remove(struct platform_device *pdev)
> +{
> +	struct fsl_spdif_priv *spdif_priv =3D platform_get_drvdata(pdev);
> +	int i;
> +
> +	imx_pcm_dma_exit(pdev);
> +	snd_soc_unregister_component(&pdev->dev);
> +
> +	for (i =3D 0; i < SPDIF_TXRATE_MAX; i++)
> +		clk_unprepare(spdif_priv->txclk[i]);
> +	clk_unprepare(spdif_priv->rxclk);
> +
> +	dev_set_drvdata(&pdev->dev, NULL);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id fsl_spdif_dt_ids[] =3D {
> +	{ .compatible =3D "fsl,imx35-spdif", },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
> +
> +static struct platform_driver fsl_spdif_driver =3D {
> +	.driver =3D {
> +		.name =3D "fsl-spdif-dai",
> +		.owner =3D THIS_MODULE,
> +		.of_match_table =3D fsl_spdif_dt_ids,
> +	},
> +	.probe =3D fsl_spdif_probe,
> +	.remove =3D 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-dai");
> diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h
> new file mode 100644
> index 0000000..f8357f6
> --- /dev/null
> +++ b/sound/soc/fsl/fsl_spdif.h
> @@ -0,0 +1,224 @@
> +/*
> + * 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 Licen=
se
> + * 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_OFFSET1	5
> +#define SRPC_CLKSRC_SEL_LOCKED_OFFSET2	2
> +#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 =3D 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_1
> +
> +enum spdif_gainsel {
> +	GAINSEL_MULTI_24 =3D 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 =3D 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 =3D 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
>=20
>=20
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev

^ permalink raw reply

* Re: [RFC] powerpc: put the common parts of the ppc64*defconfigs in a Kconfig file
From: Scott Wood @ 2013-08-16 21:34 UTC (permalink / raw)
  To: Stephen Rothwell; +Cc: ppc-dev
In-Reply-To: <20130809162426.ecdb5b034047db309ebd2b45@canb.auug.org.au>

On Fri, 2013-08-09 at 16:24 +1000, Stephen Rothwell wrote:
> We cannot put the unsetting of config options in the Kconfig file, nor
> the integer or string options.
> 
> I checked that after this we get the same .config files generated (except
> for the addition of the new PPC64_DEFCONFIG* config options.
> 
> Any thoughts?

Won't this bypass the dependency mechanism?  While the dependencies
should already be satisfied currently, nothing would prevent them from
being switched off later.  Even if you have all the dependencies listed
in the "select", what if dependencies change later?

It seems like it would be better to have a way to apply multiple
defconfigs at once, and/or have one defconfig include another.

-Scott

^ permalink raw reply

* Re: PCIE device errors after linux kernel upgrade
From: Bjorn Helgaas @ 2013-08-16 22:05 UTC (permalink / raw)
  To: Leon Ravich; +Cc: linux-pci@vger.kernel.org, linuxppc-dev
In-Reply-To: <CAPWoNkLTC+1ba3vwY0xiJVmmebvuXZBTwiyH5HAFfE8mRReaRQ@mail.gmail.com>

On Tue, Aug 6, 2013 at 11:41 PM, Leon Ravich <lravich@gmail.com> wrote:
> From comparison of pci printout from the two kernel ,
> beside the EDAC errors I noticed other strange differences:
>
> In 3.8.13 I got BAR 7 and BAR 8:
> [   39.017749] pci 0000:00:00.0: BAR 8: assigned [mem 0xc0000000-0xdfffffff]
> [   39.024530] pci 0000:00:00.0: BAR 7: can't assign io (size 0x10000)
>
> In 3.8.13 I am getting:
> [   38.931873] pci_bus 0000:01: busn_res: can not insert [bus 01-ff]
> under [bus 00-01] (conflicts with (null) [bus 00-01])
>
>
> On 6 August 2013 09:32, Leon Ravich <lravich@gmail.com> wrote:
>> Thanks Bjorn.

Is this still a problem, or have you found a solution in the meantime?

>> 1) If I understand it right this patch only removes the "pci
>> 0000:00:00.0: ignoring class 0x0b2000 (doesn't
>> match header type 01)" message , don't  care about it , had it before .

It also affects how the PCI core handles the device.  But as you said,
if you had the message before, it's probably not the cause of the
current problem.

>> 2) regarding the comparing of printouts:

Can you post the complete logs somewhere, maybe in a bugzilla or similar?

Where are the "PCIE ERR_CAP_STAT" and similar messages from?  My quick
grep didn't find them.

Bjorn

^ permalink raw reply

* Re: [RFC PATCH v2 3/4] powerpc: refactor of_get_cpu_node to support other architectures
From: Benjamin Herrenschmidt @ 2013-08-16 22:13 UTC (permalink / raw)
  To: Sudeep KarkadaNagesha
  Cc: Jonas Bonn, devicetree, Michal Simek, linux-pm, linux-kernel,
	Rob Herring, Rafael J. Wysocki, Grant Likely, linuxppc-dev,
	linux-arm-kernel
In-Reply-To: <1376674791-28244-2-git-send-email-Sudeep.KarkadaNagesha@arm.com>

On Fri, 2013-08-16 at 18:39 +0100, Sudeep KarkadaNagesha wrote:
> +static bool __of_find_n_match_cpu_property(struct device_node *cpun,
> +                       const char *prop_name, int cpu, unsigned int
> *thread)
> +{
> +       const __be32 *cell;
> +       int ac, prop_len, tid;
> +       u64 hwid;
> +
> +       ac = of_n_addr_cells(cpun);
> +       cell = of_get_property(cpun, prop_name, &prop_len);
> +       if (!cell)
> +               return false;
> +       prop_len /= sizeof(*cell);
> +       for (tid = 0; tid < prop_len; tid++) {
> +               hwid = of_read_number(cell, ac);
> +               if (arch_match_cpu_phys_id(cpu, hwid)) {
> +                       if (thread)
> +                               *thread = tid;
> +                       return true;
> +               }
> +               cell += ac;
> +       }
> +       return false;
> +}

The only problem I can see here is if "ac" is not 1, that will not work
for the ibm,ppc-interrupt-server#s case. IE. The latter is always 1 cell
per entry, only "reg" depends on #address-cells.

However that's only a theorical problem since on ppc #address-cells of
/cpus is always 1...

Cheers,
Ben.

^ permalink raw reply


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