linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/7] ASoC: add driver for Atmel I2S controller
@ 2018-05-25 12:34 Codrin Ciubotariu
  2018-05-25 12:34 ` [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock Codrin Ciubotariu
                   ` (6 more replies)
  0 siblings, 7 replies; 21+ messages in thread
From: Codrin Ciubotariu @ 2018-05-25 12:34 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, devicetree, linux-clk,
	alsa-devel, nicolas.ferre, boris.brezillon, alexandre.belloni,
	robh+dt, broonie
  Cc: Cristian.Birsan

This is a rework of Cyrille's patches named:
[PATCH v3 0/2] ASoC: add driver for Atmel I2S controller
https://lkml.org/lkml/2015/9/29/454

This is the version 4 of the series, and addresses the received feedback
on the mailing lists.

This series applies on top of asoc-next branch of broonie/sound.git.

Based on received feedback, I created a mux clock driver to select the I2S
clock source, that also includes proper devicetree bindings and nodes.
Also, I added the I2S nodes in sama5d2's devicetree, with the missing
pin muxing for the second I2S controller.

This series of patches adds support to the new Atmel I2S controller
embedded on sama5d2 SoCs.

ChangeLog

v3 -> v4
 - as suggested by Rob Herring:
   - added a clock mux driver for I2S's clock control bit;
   - more precise description of I2S's devicetree bindings;
   - removed SoC and internal detalls from bindings;
 - addressed comments from Mark Brown;
 - added devicetree nodes and pin muxing for I2S;

v2 -> v3
- fix the coding style, add some more comments and add a section dedicated
  to sama5d2 SoCs in the DT binding documentation as suggested by Mark
  Brown.

v1 -> v2
- initialize dev->dev before calling dev->caps->mck_init()


Codrin Ciubotariu (3):
  dt-bindings: clk: at91: add an I2S mux clock
  clk: at91: add I2S clock mux driver
  ARM: dts: at91: sama5d2: add I2S clock muxing nodes

Cyrille Pitchen (4):
  ASoC: atmel-i2s: dt-bindings: add DT bindings for I2S controller
  ASoC: atmel-i2s: add driver for the new Atmel I2S controller
  ARM: dts: at91: sama5d2: add nodes for I2S controllers
  ARM: dts: at91: sama5d2 Xplained: add pin muxing for I2S

 .../devicetree/bindings/clock/at91-clock.txt       |  34 +
 .../devicetree/bindings/sound/atmel-i2s.txt        |  47 ++
 arch/arm/boot/dts/at91-sama5d2_xplained.dts        |  28 +
 arch/arm/boot/dts/sama5d2.dtsi                     |  52 ++
 arch/arm/mach-at91/Kconfig                         |   4 +
 drivers/clk/at91/Makefile                          |   1 +
 drivers/clk/at91/clk-i2s-mux.c                     | 117 ++++
 sound/soc/atmel/Kconfig                            |   9 +
 sound/soc/atmel/Makefile                           |   2 +
 sound/soc/atmel/atmel-i2s.c                        | 765 +++++++++++++++++++++
 10 files changed, 1059 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/atmel-i2s.txt
 create mode 100644 drivers/clk/at91/clk-i2s-mux.c
 create mode 100644 sound/soc/atmel/atmel-i2s.c

-- 
2.7.4

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

* [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock
  2018-05-25 12:34 [PATCH v4 0/7] ASoC: add driver for Atmel I2S controller Codrin Ciubotariu
@ 2018-05-25 12:34 ` Codrin Ciubotariu
  2018-05-31  0:58   ` Rob Herring
  2018-05-25 12:34 ` [PATCH v4 2/7] clk: at91: add I2S clock mux driver Codrin Ciubotariu
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 21+ messages in thread
From: Codrin Ciubotariu @ 2018-05-25 12:34 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, devicetree, linux-clk,
	alsa-devel, nicolas.ferre, boris.brezillon, alexandre.belloni,
	robh+dt, broonie
  Cc: Cristian.Birsan

The I2S mux clock can be used to select the I2S input clock. The
available parents are the peripheral and the generated clocks.

Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
---
 .../devicetree/bindings/clock/at91-clock.txt       | 34 ++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt
index 51c259a..1c46b3c 100644
--- a/Documentation/devicetree/bindings/clock/at91-clock.txt
+++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
@@ -90,6 +90,8 @@ Required properties:
 	"atmel,sama5d2-clk-audio-pll-pmc"
 		at91 audio pll output on AUDIOPLLCLK that feeds the PMC
 		and can be used by peripheral clock or generic clock
+	"atmel,sama5d2-clk-i2s-mux":
+		at91 I2S clock source selection
 
 Required properties for SCKC node:
 - reg : defines the IO memory reserved for the SCKC.
@@ -507,3 +509,35 @@ For example:
 			atmel,clk-output-range = <0 83000000>;
 		};
 	};
+
+Required properties for I2S mux clocks:
+- #size-cells : shall be 0 (reg is used to encode I2S bus id).
+- #address-cells : shall be 1 (reg is used to encode I2S bus id).
+- name: device tree node describing a specific mux clock.
+	* #clock-cells : from common clock binding; shall be set to 0.
+	* clocks : shall be the mux clock parent phandles; shall be 2 phandles:
+	  peripheral and generated clock; the first phandle shall belong to the
+	  peripheral clock and the second one shall belong to the generated
+	  clock; "clock-indices" property can be user to specify
+	  the correct order.
+	* reg: I2S bus id of the corresponding mux clock.
+	  e.g. reg = <0>; for i2s0, reg = <1>; for i2s1
+
+For example:
+	i2s_clkmux {
+		compatible = "atmel,sama5d2-clk-i2s-mux";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		i2s0muxck: i2s0_muxclk {
+			clocks = <&i2s0_clk>, <&i2s0_gclk>;
+			#clock-cells = <0>;
+			reg = <0>;
+		};
+
+		i2s1muxck: i2s1_muxclk {
+			clocks = <&i2s1_clk>, <&i2s1_gclk>;
+			#clock-cells = <0>;
+			reg = <1>;
+		};
+	};
-- 
2.7.4

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

* [PATCH v4 2/7] clk: at91: add I2S clock mux driver
  2018-05-25 12:34 [PATCH v4 0/7] ASoC: add driver for Atmel I2S controller Codrin Ciubotariu
  2018-05-25 12:34 ` [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock Codrin Ciubotariu
@ 2018-05-25 12:34 ` Codrin Ciubotariu
  2018-05-31 15:26   ` Stephen Boyd
  2018-05-25 12:34 ` [PATCH v4 3/7] ARM: dts: at91: sama5d2: add I2S clock muxing nodes Codrin Ciubotariu
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 21+ messages in thread
From: Codrin Ciubotariu @ 2018-05-25 12:34 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, devicetree, linux-clk,
	alsa-devel, nicolas.ferre, boris.brezillon, alexandre.belloni,
	robh+dt, broonie
  Cc: Cristian.Birsan

This driver is a simple muxing driver that controls the
I2S's clock input by using syscon/regmap to change the parrent.
The available inputs can be Peripheral clock and Generated clock.

Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
---
 arch/arm/mach-at91/Kconfig     |   4 ++
 drivers/clk/at91/Makefile      |   1 +
 drivers/clk/at91/clk-i2s-mux.c | 117 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 122 insertions(+)
 create mode 100644 drivers/clk/at91/clk-i2s-mux.c

diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 1254bf9..903f23c 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -27,6 +27,7 @@ config SOC_SAMA5D2
 	select HAVE_AT91_H32MX
 	select HAVE_AT91_GENERATED_CLK
 	select HAVE_AT91_AUDIO_PLL
+	select HAVE_AT91_I2S_MUX_CLK
 	select PINCTRL_AT91PIO4
 	help
 	  Select this if ou are using one of Microchip's SAMA5D2 family SoC.
@@ -129,6 +130,9 @@ config HAVE_AT91_GENERATED_CLK
 config HAVE_AT91_AUDIO_PLL
 	bool
 
+config HAVE_AT91_I2S_MUX_CLK
+	bool
+
 config SOC_SAM_V4_V5
 	bool
 
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 082596f..facc169 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_HAVE_AT91_USB_CLK)		+= clk-usb.o
 obj-$(CONFIG_HAVE_AT91_SMD)		+= clk-smd.o
 obj-$(CONFIG_HAVE_AT91_H32MX)		+= clk-h32mx.o
 obj-$(CONFIG_HAVE_AT91_GENERATED_CLK)	+= clk-generated.o
+obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK)	+= clk-i2s-mux.o
diff --git a/drivers/clk/at91/clk-i2s-mux.c b/drivers/clk/at91/clk-i2s-mux.c
new file mode 100644
index 0000000..2d56ded
--- /dev/null
+++ b/drivers/clk/at91/clk-i2s-mux.c
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (C) 2018 Microchip Technology Inc,
+ *                     Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
+ *
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <soc/at91/atmel-sfr.h>
+
+#define	I2S_BUS_NR	2
+
+struct clk_i2s_mux {
+	struct clk_hw hw;
+	struct regmap *regmap;
+	u32 bus_id;
+};
+
+#define to_clk_i2s_mux(hw) container_of(hw, struct clk_i2s_mux, hw)
+
+static u8 clk_i2s_mux_get_parent(struct clk_hw *hw)
+{
+	struct clk_i2s_mux *mux = to_clk_i2s_mux(hw);
+	u32 val;
+
+	regmap_read(mux->regmap, AT91_SFR_I2SCLKSEL, &val);
+
+	return (val & BIT(mux->bus_id)) >> mux->bus_id;
+}
+
+static int clk_i2s_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct clk_i2s_mux *mux = to_clk_i2s_mux(hw);
+
+	return regmap_update_bits(mux->regmap, AT91_SFR_I2SCLKSEL,
+				  BIT(mux->bus_id), index << mux->bus_id);
+}
+
+const struct clk_ops clk_i2s_mux_ops = {
+	.get_parent = clk_i2s_mux_get_parent,
+	.set_parent = clk_i2s_mux_set_parent,
+	.determine_rate = __clk_mux_determine_rate,
+};
+
+static struct clk_hw * __init
+at91_clk_i2s_mux_register(struct regmap *regmap, const char *name,
+			  const char * const *parent_names,
+			  unsigned int num_parents, u32 bus_id)
+{
+	struct clk_init_data init = {};
+	struct clk_i2s_mux *i2s_ck;
+	int ret;
+
+	i2s_ck = kzalloc(sizeof(*i2s_ck), GFP_KERNEL);
+	if (!i2s_ck)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &clk_i2s_mux_ops;
+	init.parent_names = parent_names;
+	init.num_parents = num_parents;
+	init.flags = CLK_IGNORE_UNUSED;
+
+	i2s_ck->hw.init = &init;
+	i2s_ck->bus_id = bus_id;
+	i2s_ck->regmap = regmap;
+
+	ret = clk_hw_register(NULL, &i2s_ck->hw);
+	if (ret) {
+		kfree(i2s_ck);
+		return ERR_PTR(ret);
+	}
+
+	return &i2s_ck->hw;
+}
+
+static void __init of_sama5d2_clk_i2s_mux_setup(struct device_node *np)
+{
+	struct regmap *regmap_sfr;
+	u32 bus_id;
+	const char *parent_names[2];
+	struct device_node *i2s_mux_np;
+	struct clk_hw *hw;
+	int ret;
+
+	regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr");
+	if (IS_ERR(regmap_sfr))
+		return;
+
+	for_each_child_of_node(np, i2s_mux_np) {
+		if (of_property_read_u32(i2s_mux_np, "reg", &bus_id))
+			continue;
+
+		if (bus_id > I2S_BUS_NR)
+			continue;
+
+		ret = of_clk_parent_fill(i2s_mux_np, parent_names, 2);
+		if (ret != 2)
+			continue;
+
+		hw = at91_clk_i2s_mux_register(regmap_sfr, i2s_mux_np->name,
+					       parent_names, 2, bus_id);
+		if (IS_ERR(hw))
+			continue;
+
+		of_clk_add_hw_provider(i2s_mux_np, of_clk_hw_simple_get, hw);
+	}
+}
+
+CLK_OF_DECLARE(sama5d2_clk_i2s_mux, "atmel,sama5d2-clk-i2s-mux",
+	       of_sama5d2_clk_i2s_mux_setup);
-- 
2.7.4

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

* [PATCH v4 3/7] ARM: dts: at91: sama5d2: add I2S clock muxing nodes
  2018-05-25 12:34 [PATCH v4 0/7] ASoC: add driver for Atmel I2S controller Codrin Ciubotariu
  2018-05-25 12:34 ` [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock Codrin Ciubotariu
  2018-05-25 12:34 ` [PATCH v4 2/7] clk: at91: add I2S clock mux driver Codrin Ciubotariu
@ 2018-05-25 12:34 ` Codrin Ciubotariu
  2018-05-25 12:34 ` [PATCH v4 4/7] ASoC: atmel-i2s: dt-bindings: add DT bindings for I2S controller Codrin Ciubotariu
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 21+ messages in thread
From: Codrin Ciubotariu @ 2018-05-25 12:34 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, devicetree, linux-clk,
	alsa-devel, nicolas.ferre, boris.brezillon, alexandre.belloni,
	robh+dt, broonie
  Cc: Cristian.Birsan

This patch adds two clock muxes for the two I2S
buses present on sama5d2 platforms.

Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
---
 arch/arm/boot/dts/sama5d2.dtsi | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
index 61f68e5..eeb6afa 100644
--- a/arch/arm/boot/dts/sama5d2.dtsi
+++ b/arch/arm/boot/dts/sama5d2.dtsi
@@ -992,6 +992,24 @@
 						atmel,clk-output-range = <0 100000000>;
 					};
 				};
+
+				i2s_clkmux {
+					compatible = "atmel,sama5d2-clk-i2s-mux";
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					i2s0muxck: i2s0_muxclk {
+						clocks = <&i2s0_clk>, <&i2s0_gclk>;
+						#clock-cells = <0>;
+						reg = <0>;
+					};
+
+					i2s1muxck: i2s1_muxclk {
+						clocks = <&i2s1_clk>, <&i2s1_gclk>;
+						#clock-cells = <0>;
+						reg = <1>;
+					};
+				};
 			};
 
 			qspi0: spi@f0020000 {
-- 
2.7.4

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

* [PATCH v4 4/7] ASoC: atmel-i2s: dt-bindings: add DT bindings for I2S controller
  2018-05-25 12:34 [PATCH v4 0/7] ASoC: add driver for Atmel I2S controller Codrin Ciubotariu
                   ` (2 preceding siblings ...)
  2018-05-25 12:34 ` [PATCH v4 3/7] ARM: dts: at91: sama5d2: add I2S clock muxing nodes Codrin Ciubotariu
@ 2018-05-25 12:34 ` Codrin Ciubotariu
  2018-05-29 14:59   ` Applied "ASoC: atmel-i2s: dt-bindings: add DT bindings for I2S controller" to the asoc tree Mark Brown
  2018-05-25 12:34 ` [PATCH v4 5/7] ASoC: atmel-i2s: add driver for the new Atmel I2S controller Codrin Ciubotariu
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 21+ messages in thread
From: Codrin Ciubotariu @ 2018-05-25 12:34 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, devicetree, linux-clk,
	alsa-devel, nicolas.ferre, boris.brezillon, alexandre.belloni,
	robh+dt, broonie
  Cc: Cristian.Birsan, Cyrille Pitchen

From: Cyrille Pitchen <cyrille.pitchen@atmel.com>

This patch adds DT bindings for the new Atmel I2S controller embedded
inside sama5d2x SoCs.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
---

Changes in v4:
	- more specific description for dmas, dma-names, clocks, clock-names
	  properties;
	- removed SoC and internal details of the block IP;
	- added description for the new clock mux phandle;

 .../devicetree/bindings/sound/atmel-i2s.txt        | 47 ++++++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/atmel-i2s.txt

diff --git a/Documentation/devicetree/bindings/sound/atmel-i2s.txt b/Documentation/devicetree/bindings/sound/atmel-i2s.txt
new file mode 100644
index 0000000..735368b
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/atmel-i2s.txt
@@ -0,0 +1,47 @@
+* Atmel I2S controller
+
+Required properties:
+- compatible:     Should be "atmel,sama5d2-i2s".
+- reg:            Should be the physical base address of the controller and the
+                  length of memory mapped region.
+- interrupts:     Should contain the interrupt for the controller.
+- dmas:           Should be one per channel name listed in the dma-names property,
+                  as described in atmel-dma.txt and dma.txt files.
+- dma-names:      Two dmas have to be defined, "tx" and "rx".
+                  This IP also supports one shared channel for both rx and tx;
+                  if this mode is used, one "rx-tx" name must be used.
+- clocks:         Must contain an entry for each entry in clock-names.
+                  Please refer to clock-bindings.txt.
+- clock-names:    Should be one of each entry matching the clocks phandles list:
+                  - "pclk" (peripheral clock) Required.
+                  - "gclk" (generated clock) Optional (1).
+                  - "aclk" (Audio PLL clock) Optional (1).
+                  - "muxclk" (I2S mux clock) Optional (1).
+
+Optional properties:
+- pinctrl-0:      Should specify pin control groups used for this controller.
+- princtrl-names: Should contain only one value - "default".
+
+
+(1) : Only the peripheral clock is required. The generated clock, the Audio
+      PLL clock adn the I2S mux clock are optional and should only be set
+      together, when Master Mode is required.
+
+Example:
+
+	i2s@f8050000 {
+		compatible = "atmel,sama5d2-i2s";
+		reg = <0xf8050000 0x300>;
+		interrupts = <54 IRQ_TYPE_LEVEL_HIGH 7>;
+		dmas = <&dma0
+			(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+			 AT91_XDMAC_DT_PERID(31))>,
+		       <&dma0
+			(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+			 AT91_XDMAC_DT_PERID(32))>;
+		dma-names = "tx", "rx";
+		clocks = <&i2s0_clk>, <&i2s0_gclk>, <&audio_pll_pmc>, <&i2s0muxck>;
+		clock-names = "pclk", "gclk", "aclk", "muxclk";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_i2s0_default>;
+	};
-- 
2.7.4

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

* [PATCH v4 5/7] ASoC: atmel-i2s: add driver for the new Atmel I2S controller
  2018-05-25 12:34 [PATCH v4 0/7] ASoC: add driver for Atmel I2S controller Codrin Ciubotariu
                   ` (3 preceding siblings ...)
  2018-05-25 12:34 ` [PATCH v4 4/7] ASoC: atmel-i2s: dt-bindings: add DT bindings for I2S controller Codrin Ciubotariu
@ 2018-05-25 12:34 ` Codrin Ciubotariu
  2018-05-29 14:59   ` Applied "ASoC: atmel-i2s: add driver for the new Atmel I2S controller" to the asoc tree Mark Brown
  2018-05-25 12:34 ` [PATCH v4 6/7] ARM: dts: at91: sama5d2: add nodes for I2S controllers Codrin Ciubotariu
  2018-05-25 12:34 ` [PATCH v4 7/7] ARM: dts: at91: sama5d2 Xplained: add pin muxing for I2S Codrin Ciubotariu
  6 siblings, 1 reply; 21+ messages in thread
From: Codrin Ciubotariu @ 2018-05-25 12:34 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, devicetree, linux-clk,
	alsa-devel, nicolas.ferre, boris.brezillon, alexandre.belloni,
	robh+dt, broonie
  Cc: Cristian.Birsan, Cyrille Pitchen

From: Cyrille Pitchen <cyrille.pitchen@atmel.com>

This patch adds support for the Atmel I2S controller embedded into
sama5d2x SoCs.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
---

Changes in v4:
	- treated -EPROBE_DEFER on probe;
	- replaced ternary operator with if;
	- return -EINVAL if we don't support the number of audio channels.

 sound/soc/atmel/Kconfig     |   9 +
 sound/soc/atmel/Makefile    |   2 +
 sound/soc/atmel/atmel-i2s.c | 765 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 776 insertions(+)
 create mode 100644 sound/soc/atmel/atmel-i2s.c

diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index dcee145..64b784e 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -88,4 +88,13 @@ config SND_ATMEL_SOC_TSE850_PCM5142
 	help
 	  Say Y if you want to add support for the ASoC driver for the
 	  Axentia TSE-850 with a PCM5142 codec.
+
+config SND_ATMEL_SOC_I2S
+	tristate "Atmel ASoC driver for boards using I2S"
+	depends on OF && (ARCH_AT91 || COMPILE_TEST)
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	select REGMAP_MMIO
+	help
+	  Say Y or M if you want to add support for Atmel ASoc driver for boards
+	  using I2S.
 endif
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index 4440646..cd87cb4 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -3,10 +3,12 @@
 snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
 snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
 snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
+snd-soc-atmel-i2s-objs := atmel-i2s.o
 
 obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
 obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
 obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
+obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o
 
 # AT91 Machine Support
 snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c
new file mode 100644
index 0000000..5d3b5af
--- /dev/null
+++ b/sound/soc/atmel/atmel-i2s.c
@@ -0,0 +1,765 @@
+/*
+ * Driver for Atmel I2S controller
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ *
+ * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#define ATMEL_I2SC_MAX_TDM_CHANNELS	8
+
+/*
+ * ---- I2S Controller Register map ----
+ */
+#define ATMEL_I2SC_CR		0x0000	/* Control Register */
+#define ATMEL_I2SC_MR		0x0004	/* Mode Register */
+#define ATMEL_I2SC_SR		0x0008	/* Status Register */
+#define ATMEL_I2SC_SCR		0x000c	/* Status Clear Register */
+#define ATMEL_I2SC_SSR		0x0010	/* Status Set Register */
+#define ATMEL_I2SC_IER		0x0014	/* Interrupt Enable Register */
+#define ATMEL_I2SC_IDR		0x0018	/* Interrupt Disable Register */
+#define ATMEL_I2SC_IMR		0x001c	/* Interrupt Mask Register */
+#define ATMEL_I2SC_RHR		0x0020	/* Receiver Holding Register */
+#define ATMEL_I2SC_THR		0x0024	/* Transmitter Holding Register */
+#define ATMEL_I2SC_VERSION	0x0028	/* Version Register */
+
+/*
+ * ---- Control Register (Write-only) ----
+ */
+#define ATMEL_I2SC_CR_RXEN	BIT(0)	/* Receiver Enable */
+#define ATMEL_I2SC_CR_RXDIS	BIT(1)	/* Receiver Disable */
+#define ATMEL_I2SC_CR_CKEN	BIT(2)	/* Clock Enable */
+#define ATMEL_I2SC_CR_CKDIS	BIT(3)	/* Clock Disable */
+#define ATMEL_I2SC_CR_TXEN	BIT(4)	/* Transmitter Enable */
+#define ATMEL_I2SC_CR_TXDIS	BIT(5)	/* Transmitter Disable */
+#define ATMEL_I2SC_CR_SWRST	BIT(7)	/* Software Reset */
+
+/*
+ * ---- Mode Register (Read/Write) ----
+ */
+#define ATMEL_I2SC_MR_MODE_MASK		GENMASK(0, 0)
+#define ATMEL_I2SC_MR_MODE_SLAVE	(0 << 0)
+#define ATMEL_I2SC_MR_MODE_MASTER	(1 << 0)
+
+#define ATMEL_I2SC_MR_DATALENGTH_MASK		GENMASK(4, 2)
+#define ATMEL_I2SC_MR_DATALENGTH_32_BITS	(0 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_24_BITS	(1 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_20_BITS	(2 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_18_BITS	(3 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_16_BITS	(4 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_16_BITS_COMPACT	(5 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_8_BITS		(6 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_8_BITS_COMPACT	(7 << 2)
+
+#define ATMEL_I2SC_MR_FORMAT_MASK	GENMASK(7, 6)
+#define ATMEL_I2SC_MR_FORMAT_I2S	(0 << 6)
+#define ATMEL_I2SC_MR_FORMAT_LJ		(1 << 6)  /* Left Justified */
+#define ATMEL_I2SC_MR_FORMAT_TDM	(2 << 6)
+#define ATMEL_I2SC_MR_FORMAT_TDMLJ	(3 << 6)
+
+/* Left audio samples duplicated to right audio channel */
+#define ATMEL_I2SC_MR_RXMONO		BIT(8)
+
+/* Receiver uses one DMA channel ... */
+#define ATMEL_I2SC_MR_RXDMA_MASK	GENMASK(9, 9)
+#define ATMEL_I2SC_MR_RXDMA_SINGLE	(0 << 9)  /* for all audio channels */
+#define ATMEL_I2SC_MR_RXDMA_MULTIPLE	(1 << 9)  /* per audio channel */
+
+/* I2SDO output of I2SC is internally connected to I2SDI input */
+#define ATMEL_I2SC_MR_RXLOOP		BIT(10)
+
+/* Left audio samples duplicated to right audio channel */
+#define ATMEL_I2SC_MR_TXMONO		BIT(12)
+
+/* Transmitter uses one DMA channel ... */
+#define ATMEL_I2SC_MR_TXDMA_MASK	GENMASK(13, 13)
+#define ATMEL_I2SC_MR_TXDMA_SINGLE	(0 << 13)  /* for all audio channels */
+#define ATMEL_I2SC_MR_TXDME_MULTIPLE	(1 << 13)  /* per audio channel */
+
+/* x sample transmitted when underrun */
+#define ATMEL_I2SC_MR_TXSAME_MASK	GENMASK(14, 14)
+#define ATMEL_I2SC_MR_TXSAME_ZERO	(0 << 14)  /* Zero sample */
+#define ATMEL_I2SC_MR_TXSAME_PREVIOUS	(1 << 14)  /* Previous sample */
+
+/* Audio Clock to I2SC Master Clock ratio */
+#define ATMEL_I2SC_MR_IMCKDIV_MASK	GENMASK(21, 16)
+#define ATMEL_I2SC_MR_IMCKDIV(div) \
+	(((div) << 16) & ATMEL_I2SC_MR_IMCKDIV_MASK)
+
+/* Master Clock to fs ratio */
+#define ATMEL_I2SC_MR_IMCKFS_MASK	GENMASK(29, 24)
+#define ATMEL_I2SC_MR_IMCKFS(fs) \
+	(((fs) << 24) & ATMEL_I2SC_MR_IMCKFS_MASK)
+
+/* Master Clock mode */
+#define ATMEL_I2SC_MR_IMCKMODE_MASK	GENMASK(30, 30)
+/* 0: No master clock generated (selected clock drives I2SCK pin) */
+#define ATMEL_I2SC_MR_IMCKMODE_I2SCK	(0 << 30)
+/* 1: master clock generated (internally generated clock drives I2SMCK pin) */
+#define ATMEL_I2SC_MR_IMCKMODE_I2SMCK	(1 << 30)
+
+/* Slot Width */
+/* 0: slot is 32 bits wide for DATALENGTH = 18/20/24 bits. */
+/* 1: slot is 24 bits wide for DATALENGTH = 18/20/24 bits. */
+#define ATMEL_I2SC_MR_IWS		BIT(31)
+
+/*
+ * ---- Status Registers ----
+ */
+#define ATMEL_I2SC_SR_RXEN	BIT(0)	/* Receiver Enabled */
+#define ATMEL_I2SC_SR_RXRDY	BIT(1)	/* Receive Ready */
+#define ATMEL_I2SC_SR_RXOR	BIT(2)	/* Receive Overrun */
+
+#define ATMEL_I2SC_SR_TXEN	BIT(4)	/* Transmitter Enabled */
+#define ATMEL_I2SC_SR_TXRDY	BIT(5)	/* Transmit Ready */
+#define ATMEL_I2SC_SR_TXUR	BIT(6)	/* Transmit Underrun */
+
+/* Receive Overrun Channel */
+#define ATMEL_I2SC_SR_RXORCH_MASK	GENMASK(15, 8)
+#define ATMEL_I2SC_SR_RXORCH(ch)	(1 << (((ch) & 0x7) + 8))
+
+/* Transmit Underrun Channel */
+#define ATMEL_I2SC_SR_TXURCH_MASK	GENMASK(27, 20)
+#define ATMEL_I2SC_SR_TXURCH(ch)	(1 << (((ch) & 0x7) + 20))
+
+/*
+ * ---- Interrupt Enable/Disable/Mask Registers ----
+ */
+#define ATMEL_I2SC_INT_RXRDY	ATMEL_I2SC_SR_RXRDY
+#define ATMEL_I2SC_INT_RXOR	ATMEL_I2SC_SR_RXOR
+#define ATMEL_I2SC_INT_TXRDY	ATMEL_I2SC_SR_TXRDY
+#define ATMEL_I2SC_INT_TXUR	ATMEL_I2SC_SR_TXUR
+
+static const struct regmap_config atmel_i2s_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = ATMEL_I2SC_VERSION,
+};
+
+struct atmel_i2s_gck_param {
+	int		fs;
+	unsigned long	mck;
+	int		imckdiv;
+	int		imckfs;
+};
+
+#define I2S_MCK_12M288		12288000UL
+#define I2S_MCK_11M2896		11289600UL
+
+/* mck = (32 * (imckfs+1) / (imckdiv+1)) * fs */
+static const struct atmel_i2s_gck_param gck_params[] = {
+	/* mck = 12.288MHz */
+	{  8000, I2S_MCK_12M288, 0, 47},	/* mck = 1536 fs */
+	{ 16000, I2S_MCK_12M288, 1, 47},	/* mck =  768 fs */
+	{ 24000, I2S_MCK_12M288, 3, 63},	/* mck =  512 fs */
+	{ 32000, I2S_MCK_12M288, 3, 47},	/* mck =  384 fs */
+	{ 48000, I2S_MCK_12M288, 7, 63},	/* mck =  256 fs */
+	{ 64000, I2S_MCK_12M288, 7, 47},	/* mck =  192 fs */
+	{ 96000, I2S_MCK_12M288, 7, 31},	/* mck =  128 fs */
+	{192000, I2S_MCK_12M288, 7, 15},	/* mck =   64 fs */
+
+	/* mck = 11.2896MHz */
+	{ 11025, I2S_MCK_11M2896, 1, 63},	/* mck = 1024 fs */
+	{ 22050, I2S_MCK_11M2896, 3, 63},	/* mck =  512 fs */
+	{ 44100, I2S_MCK_11M2896, 7, 63},	/* mck =  256 fs */
+	{ 88200, I2S_MCK_11M2896, 7, 31},	/* mck =  128 fs */
+	{176400, I2S_MCK_11M2896, 7, 15},	/* mck =   64 fs */
+};
+
+struct atmel_i2s_dev;
+
+struct atmel_i2s_caps {
+	int	(*mck_init)(struct atmel_i2s_dev *, struct device_node *np);
+};
+
+struct atmel_i2s_dev {
+	struct device				*dev;
+	struct regmap				*regmap;
+	struct clk				*pclk;
+	struct clk				*gclk;
+	struct clk				*aclk;
+	struct snd_dmaengine_dai_dma_data	playback;
+	struct snd_dmaengine_dai_dma_data	capture;
+	unsigned int				fmt;
+	const struct atmel_i2s_gck_param	*gck_param;
+	const struct atmel_i2s_caps		*caps;
+};
+
+static irqreturn_t atmel_i2s_interrupt(int irq, void *dev_id)
+{
+	struct atmel_i2s_dev *dev = dev_id;
+	unsigned int sr, imr, pending, ch, mask;
+	irqreturn_t ret = IRQ_NONE;
+
+	regmap_read(dev->regmap, ATMEL_I2SC_SR, &sr);
+	regmap_read(dev->regmap, ATMEL_I2SC_IMR, &imr);
+	pending = sr & imr;
+
+	if (!pending)
+		return IRQ_NONE;
+
+	if (pending & ATMEL_I2SC_INT_RXOR) {
+		mask = ATMEL_I2SC_SR_RXOR;
+
+		for (ch = 0; ch < ATMEL_I2SC_MAX_TDM_CHANNELS; ++ch) {
+			if (sr & ATMEL_I2SC_SR_RXORCH(ch)) {
+				mask |= ATMEL_I2SC_SR_RXORCH(ch);
+				dev_err(dev->dev,
+					"RX overrun on channel %d\n", ch);
+			}
+		}
+		regmap_write(dev->regmap, ATMEL_I2SC_SCR, mask);
+		ret = IRQ_HANDLED;
+	}
+
+	if (pending & ATMEL_I2SC_INT_TXUR) {
+		mask = ATMEL_I2SC_SR_TXUR;
+
+		for (ch = 0; ch < ATMEL_I2SC_MAX_TDM_CHANNELS; ++ch) {
+			if (sr & ATMEL_I2SC_SR_TXURCH(ch)) {
+				mask |= ATMEL_I2SC_SR_TXURCH(ch);
+				dev_err(dev->dev,
+					"TX underrun on channel %d\n", ch);
+			}
+		}
+		regmap_write(dev->regmap, ATMEL_I2SC_SCR, mask);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+#define ATMEL_I2S_RATES		SNDRV_PCM_RATE_8000_192000
+
+#define ATMEL_I2S_FORMATS	(SNDRV_PCM_FMTBIT_S8 |		\
+				 SNDRV_PCM_FMTBIT_S16_LE |	\
+				 SNDRV_PCM_FMTBIT_S18_3LE |	\
+				 SNDRV_PCM_FMTBIT_S20_3LE |	\
+				 SNDRV_PCM_FMTBIT_S24_3LE |	\
+				 SNDRV_PCM_FMTBIT_S24_LE |	\
+				 SNDRV_PCM_FMTBIT_S32_LE)
+
+static int atmel_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+	dev->fmt = fmt;
+	return 0;
+}
+
+static int atmel_i2s_prepare(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+	bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	unsigned int rhr, sr = 0;
+
+	if (is_playback) {
+		regmap_read(dev->regmap, ATMEL_I2SC_SR, &sr);
+		if (sr & ATMEL_I2SC_SR_RXRDY) {
+			/*
+			 * The RX Ready flag should not be set. However if here,
+			 * we flush (read) the Receive Holding Register to start
+			 * from a clean state.
+			 */
+			dev_dbg(dev->dev, "RXRDY is set\n");
+			regmap_read(dev->regmap, ATMEL_I2SC_RHR, &rhr);
+		}
+	}
+
+	return 0;
+}
+
+static int atmel_i2s_get_gck_param(struct atmel_i2s_dev *dev, int fs)
+{
+	int i, best;
+
+	if (!dev->gclk || !dev->aclk) {
+		dev_err(dev->dev, "cannot generate the I2S Master Clock\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Find the best possible settings to generate the I2S Master Clock
+	 * from the PLL Audio.
+	 */
+	dev->gck_param = NULL;
+	best = INT_MAX;
+	for (i = 0; i < ARRAY_SIZE(gck_params); ++i) {
+		const struct atmel_i2s_gck_param *gck_param = &gck_params[i];
+		int val = abs(fs - gck_param->fs);
+
+		if (val < best) {
+			best = val;
+			dev->gck_param = gck_param;
+		}
+	}
+
+	return 0;
+}
+
+static int atmel_i2s_hw_params(struct snd_pcm_substream *substream,
+			       struct snd_pcm_hw_params *params,
+			       struct snd_soc_dai *dai)
+{
+	struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+	bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	unsigned int mr = 0;
+	int ret;
+
+	switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		mr |= ATMEL_I2SC_MR_FORMAT_I2S;
+		break;
+
+	default:
+		dev_err(dev->dev, "unsupported bus format\n");
+		return -EINVAL;
+	}
+
+	switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		/* codec is slave, so cpu is master */
+		mr |= ATMEL_I2SC_MR_MODE_MASTER;
+		ret = atmel_i2s_get_gck_param(dev, params_rate(params));
+		if (ret)
+			return ret;
+		break;
+
+	case SND_SOC_DAIFMT_CBM_CFM:
+		/* codec is master, so cpu is slave */
+		mr |= ATMEL_I2SC_MR_MODE_SLAVE;
+		dev->gck_param = NULL;
+		break;
+
+	default:
+		dev_err(dev->dev, "unsupported master/slave mode\n");
+		return -EINVAL;
+	}
+
+	switch (params_channels(params)) {
+	case 1:
+		if (is_playback)
+			mr |= ATMEL_I2SC_MR_TXMONO;
+		else
+			mr |= ATMEL_I2SC_MR_RXMONO;
+		break;
+	case 2:
+		break;
+	default:
+		dev_err(dev->dev, "unsupported number of audio channels\n");
+		return -EINVAL;
+	}
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_8_BITS;
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_LE:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_16_BITS;
+		break;
+
+	case SNDRV_PCM_FORMAT_S18_3LE:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_18_BITS | ATMEL_I2SC_MR_IWS;
+		break;
+
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_20_BITS | ATMEL_I2SC_MR_IWS;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_24_BITS | ATMEL_I2SC_MR_IWS;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_24_BITS;
+		break;
+
+	case SNDRV_PCM_FORMAT_S32_LE:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_32_BITS;
+		break;
+
+	default:
+		dev_err(dev->dev, "unsupported size/endianness for audio samples\n");
+		return -EINVAL;
+	}
+
+	return regmap_write(dev->regmap, ATMEL_I2SC_MR, mr);
+}
+
+static int atmel_i2s_switch_mck_generator(struct atmel_i2s_dev *dev,
+					  bool enabled)
+{
+	unsigned int mr, mr_mask;
+	unsigned long aclk_rate;
+	int ret;
+
+	mr = 0;
+	mr_mask = (ATMEL_I2SC_MR_IMCKDIV_MASK |
+		   ATMEL_I2SC_MR_IMCKFS_MASK |
+		   ATMEL_I2SC_MR_IMCKMODE_MASK);
+
+	if (!enabled) {
+		/* Disable the I2S Master Clock generator. */
+		ret = regmap_write(dev->regmap, ATMEL_I2SC_CR,
+				   ATMEL_I2SC_CR_CKDIS);
+		if (ret)
+			return ret;
+
+		/* Reset the I2S Master Clock generator settings. */
+		ret = regmap_update_bits(dev->regmap, ATMEL_I2SC_MR,
+					 mr_mask, mr);
+		if (ret)
+			return ret;
+
+		/* Disable/unprepare the PMC generated clock. */
+		clk_disable_unprepare(dev->gclk);
+
+		/* Disable/unprepare the PLL audio clock. */
+		clk_disable_unprepare(dev->aclk);
+		return 0;
+	}
+
+	if (!dev->gck_param)
+		return -EINVAL;
+
+	aclk_rate = dev->gck_param->mck * (dev->gck_param->imckdiv + 1);
+
+	/* Fist change the PLL audio clock frequency ... */
+	ret = clk_set_rate(dev->aclk, aclk_rate);
+	if (ret)
+		return ret;
+
+	/*
+	 * ... then set the PMC generated clock rate to the very same frequency
+	 * to set the gclk parent to aclk.
+	 */
+	ret = clk_set_rate(dev->gclk, aclk_rate);
+	if (ret)
+		return ret;
+
+	/* Prepare and enable the PLL audio clock first ... */
+	ret = clk_prepare_enable(dev->aclk);
+	if (ret)
+		return ret;
+
+	/* ... then prepare and enable the PMC generated clock. */
+	ret = clk_prepare_enable(dev->gclk);
+	if (ret)
+		return ret;
+
+	/* Update the Mode Register to generate the I2S Master Clock. */
+	mr |= ATMEL_I2SC_MR_IMCKDIV(dev->gck_param->imckdiv);
+	mr |= ATMEL_I2SC_MR_IMCKFS(dev->gck_param->imckfs);
+	mr |= ATMEL_I2SC_MR_IMCKMODE_I2SMCK;
+	ret = regmap_update_bits(dev->regmap, ATMEL_I2SC_MR, mr_mask, mr);
+	if (ret)
+		return ret;
+
+	/* Finally enable the I2S Master Clock generator. */
+	return regmap_write(dev->regmap, ATMEL_I2SC_CR,
+			    ATMEL_I2SC_CR_CKEN);
+}
+
+static int atmel_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+			     struct snd_soc_dai *dai)
+{
+	struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+	bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	bool is_master, mck_enabled;
+	unsigned int cr, mr;
+	int err;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		cr = is_playback ? ATMEL_I2SC_CR_TXEN : ATMEL_I2SC_CR_RXEN;
+		mck_enabled = true;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		cr = is_playback ? ATMEL_I2SC_CR_TXDIS : ATMEL_I2SC_CR_RXDIS;
+		mck_enabled = false;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Read the Mode Register to retrieve the master/slave state. */
+	err = regmap_read(dev->regmap, ATMEL_I2SC_MR, &mr);
+	if (err)
+		return err;
+	is_master = (mr & ATMEL_I2SC_MR_MODE_MASK) == ATMEL_I2SC_MR_MODE_MASTER;
+
+	/* If master starts, enable the audio clock. */
+	if (is_master && mck_enabled)
+		err = atmel_i2s_switch_mck_generator(dev, true);
+	if (err)
+		return err;
+
+	err = regmap_write(dev->regmap, ATMEL_I2SC_CR, cr);
+	if (err)
+		return err;
+
+	/* If master stops, disable the audio clock. */
+	if (is_master && !mck_enabled)
+		err = atmel_i2s_switch_mck_generator(dev, false);
+
+	return err;
+}
+
+static const struct snd_soc_dai_ops atmel_i2s_dai_ops = {
+	.prepare	= atmel_i2s_prepare,
+	.trigger	= atmel_i2s_trigger,
+	.hw_params	= atmel_i2s_hw_params,
+	.set_fmt	= atmel_i2s_set_dai_fmt,
+};
+
+static int atmel_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+	struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+	snd_soc_dai_init_dma_data(dai, &dev->playback, &dev->capture);
+	return 0;
+}
+
+static struct snd_soc_dai_driver atmel_i2s_dai = {
+	.probe	= atmel_i2s_dai_probe,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = ATMEL_I2S_RATES,
+		.formats = ATMEL_I2S_FORMATS,
+	},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = ATMEL_I2S_RATES,
+		.formats = ATMEL_I2S_FORMATS,
+	},
+	.ops = &atmel_i2s_dai_ops,
+	.symmetric_rates = 1,
+};
+
+static const struct snd_soc_component_driver atmel_i2s_component = {
+	.name	= "atmel-i2s",
+};
+
+static int atmel_i2s_sama5d2_mck_init(struct atmel_i2s_dev *dev,
+				      struct device_node *np)
+{
+	struct clk *muxclk;
+	int err;
+
+	if (!dev->gclk)
+		return 0;
+
+	/* muxclk is optional, so we return error for probe defer only */
+	muxclk = devm_clk_get(dev->dev, "muxclk");
+	if (IS_ERR(muxclk)) {
+		err = PTR_ERR(muxclk);
+		if (err == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_warn(dev->dev,
+			 "failed to get the I2S clock control: %d\n", err);
+		return 0;
+	}
+
+	return clk_set_parent(muxclk, dev->gclk);
+}
+
+static const struct atmel_i2s_caps atmel_i2s_sama5d2_caps = {
+	.mck_init = atmel_i2s_sama5d2_mck_init,
+};
+
+static const struct of_device_id atmel_i2s_dt_ids[] = {
+	{
+		.compatible = "atmel,sama5d2-i2s",
+		.data = (void *)&atmel_i2s_sama5d2_caps,
+	},
+
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_i2s_dt_ids);
+
+static int atmel_i2s_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	const struct of_device_id *match;
+	struct atmel_i2s_dev *dev;
+	struct resource *mem;
+	struct regmap *regmap;
+	void __iomem *base;
+	int irq;
+	int err = -ENXIO;
+	unsigned int pcm_flags = 0;
+	unsigned int version;
+
+	/* Get memory for driver data. */
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	/* Get hardware capabilities. */
+	match = of_match_node(atmel_i2s_dt_ids, np);
+	if (match)
+		dev->caps = match->data;
+
+	/* Map I/O registers. */
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, mem);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	regmap = devm_regmap_init_mmio(&pdev->dev, base,
+				       &atmel_i2s_regmap_config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	/* Request IRQ. */
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	err = devm_request_irq(&pdev->dev, irq, atmel_i2s_interrupt, 0,
+			       dev_name(&pdev->dev), dev);
+	if (err)
+		return err;
+
+	/* Get the peripheral clock. */
+	dev->pclk = devm_clk_get(&pdev->dev, "pclk");
+	if (IS_ERR(dev->pclk)) {
+		err = PTR_ERR(dev->pclk);
+		dev_err(&pdev->dev,
+			"failed to get the peripheral clock: %d\n", err);
+		return err;
+	}
+
+	/* Get audio clocks to generate the I2S Master Clock (I2S_MCK) */
+	dev->aclk = devm_clk_get(&pdev->dev, "aclk");
+	dev->gclk = devm_clk_get(&pdev->dev, "gclk");
+	if (IS_ERR(dev->aclk) && IS_ERR(dev->gclk)) {
+		if (PTR_ERR(dev->aclk) == -EPROBE_DEFER ||
+		    PTR_ERR(dev->gclk) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		/* Master Mode not supported */
+		dev->aclk = NULL;
+		dev->gclk = NULL;
+	} else if (IS_ERR(dev->gclk)) {
+		err = PTR_ERR(dev->gclk);
+		dev_err(&pdev->dev,
+			"failed to get the PMC generated clock: %d\n", err);
+		return err;
+	} else if (IS_ERR(dev->aclk)) {
+		err = PTR_ERR(dev->aclk);
+		dev_err(&pdev->dev,
+			"failed to get the PLL audio clock: %d\n", err);
+		return err;
+	}
+
+	dev->dev = &pdev->dev;
+	dev->regmap = regmap;
+	platform_set_drvdata(pdev, dev);
+
+	/* Do hardware specific settings to initialize I2S_MCK generator */
+	if (dev->caps && dev->caps->mck_init) {
+		err = dev->caps->mck_init(dev, np);
+		if (err)
+			return err;
+	}
+
+	/* Enable the peripheral clock. */
+	err = clk_prepare_enable(dev->pclk);
+	if (err)
+		return err;
+
+	/* Get IP version. */
+	regmap_read(dev->regmap, ATMEL_I2SC_VERSION, &version);
+	dev_info(&pdev->dev, "hw version: %#x\n", version);
+
+	/* Enable error interrupts. */
+	regmap_write(dev->regmap, ATMEL_I2SC_IER,
+		     ATMEL_I2SC_INT_RXOR | ATMEL_I2SC_INT_TXUR);
+
+	err = devm_snd_soc_register_component(&pdev->dev,
+					      &atmel_i2s_component,
+					      &atmel_i2s_dai, 1);
+	if (err) {
+		dev_err(&pdev->dev, "failed to register DAI: %d\n", err);
+		clk_disable_unprepare(dev->pclk);
+		return err;
+	}
+
+	/* Prepare DMA config. */
+	dev->playback.addr	= (dma_addr_t)mem->start + ATMEL_I2SC_THR;
+	dev->playback.maxburst	= 1;
+	dev->capture.addr	= (dma_addr_t)mem->start + ATMEL_I2SC_RHR;
+	dev->capture.maxburst	= 1;
+
+	if (of_property_match_string(np, "dma-names", "rx-tx") == 0)
+		pcm_flags |= SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX;
+	err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, pcm_flags);
+	if (err) {
+		dev_err(&pdev->dev, "failed to register PCM: %d\n", err);
+		clk_disable_unprepare(dev->pclk);
+		return err;
+	}
+
+	return 0;
+}
+
+static int atmel_i2s_remove(struct platform_device *pdev)
+{
+	struct atmel_i2s_dev *dev = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(dev->pclk);
+
+	return 0;
+}
+
+static struct platform_driver atmel_i2s_driver = {
+	.driver		= {
+		.name	= "atmel_i2s",
+		.of_match_table	= of_match_ptr(atmel_i2s_dt_ids),
+	},
+	.probe		= atmel_i2s_probe,
+	.remove		= atmel_i2s_remove,
+};
+module_platform_driver(atmel_i2s_driver);
+
+MODULE_DESCRIPTION("Atmel I2S Controller driver");
+MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

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

* [PATCH v4 6/7] ARM: dts: at91: sama5d2: add nodes for I2S controllers
  2018-05-25 12:34 [PATCH v4 0/7] ASoC: add driver for Atmel I2S controller Codrin Ciubotariu
                   ` (4 preceding siblings ...)
  2018-05-25 12:34 ` [PATCH v4 5/7] ASoC: atmel-i2s: add driver for the new Atmel I2S controller Codrin Ciubotariu
@ 2018-05-25 12:34 ` Codrin Ciubotariu
  2018-05-25 12:34 ` [PATCH v4 7/7] ARM: dts: at91: sama5d2 Xplained: add pin muxing for I2S Codrin Ciubotariu
  6 siblings, 0 replies; 21+ messages in thread
From: Codrin Ciubotariu @ 2018-05-25 12:34 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, devicetree, linux-clk,
	alsa-devel, nicolas.ferre, boris.brezillon, alexandre.belloni,
	robh+dt, broonie
  Cc: Cristian.Birsan, Cyrille Pitchen

From: Cyrille Pitchen <cyrille.pitchen@atmel.com>

This patch adds DT nodes for I2S0 and I2S1. It also adds an alias for
each I2S node.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
[codrin.ciubotariu@microchip.com: added phandle to new mux clock]
Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
---
 arch/arm/boot/dts/sama5d2.dtsi | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
index eeb6afa..eca73ce 100644
--- a/arch/arm/boot/dts/sama5d2.dtsi
+++ b/arch/arm/boot/dts/sama5d2.dtsi
@@ -58,6 +58,8 @@
 		serial1 = &uart3;
 		tcb0 = &tcb0;
 		tcb1 = &tcb1;
+		i2s0 = &i2s0;
+		i2s1 = &i2s1;
 	};
 
 	cpus {
@@ -1313,6 +1315,22 @@
 				clocks = <&clk32k>;
 			};
 
+			i2s0: i2s@f8050000 {
+				compatible = "atmel,sama5d2-i2s";
+				reg = <0xf8050000 0x100>;
+				interrupts = <54 IRQ_TYPE_LEVEL_HIGH 7>;
+				dmas = <&dma0
+					(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+					 AT91_XDMAC_DT_PERID(31))>,
+				       <&dma0
+					(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+					 AT91_XDMAC_DT_PERID(32))>;
+				dma-names = "tx", "rx";
+				clocks = <&i2s0_clk>, <&i2s0_gclk>, <&audio_pll_pmc>, <&i2s0muxck>;
+				clock-names = "pclk", "gclk", "aclk", "muxclk";
+				status = "disabled";
+			};
+
 			can0: can@f8054000 {
 				compatible = "bosch,m_can";
 				reg = <0xf8054000 0x4000>, <0x210000 0x4000>;
@@ -1506,6 +1524,22 @@
 				status = "disabled";
 			};
 
+			i2s1: i2s@fc04c000 {
+				compatible = "atmel,sama5d2-i2s";
+				reg = <0xfc04c000 0x100>;
+				interrupts = <55 IRQ_TYPE_LEVEL_HIGH 7>;
+				dmas = <&dma0
+					(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+					 AT91_XDMAC_DT_PERID(33))>,
+				       <&dma0
+					(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+					 AT91_XDMAC_DT_PERID(34))>;
+				dma-names = "tx", "rx";
+				clocks = <&i2s1_clk>, <&i2s1_gclk>, <&audio_pll_pmc>, <&i2s1muxck>;
+				clock-names = "pclk", "gclk", "aclk", "muxclk";
+				status = "disabled";
+			};
+
 			can1: can@fc050000 {
 				compatible = "bosch,m_can";
 				reg = <0xfc050000 0x4000>, <0x210000 0x4000>;
-- 
2.7.4

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

* [PATCH v4 7/7] ARM: dts: at91: sama5d2 Xplained: add pin muxing for I2S
  2018-05-25 12:34 [PATCH v4 0/7] ASoC: add driver for Atmel I2S controller Codrin Ciubotariu
                   ` (5 preceding siblings ...)
  2018-05-25 12:34 ` [PATCH v4 6/7] ARM: dts: at91: sama5d2: add nodes for I2S controllers Codrin Ciubotariu
@ 2018-05-25 12:34 ` Codrin Ciubotariu
  6 siblings, 0 replies; 21+ messages in thread
From: Codrin Ciubotariu @ 2018-05-25 12:34 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, devicetree, linux-clk,
	alsa-devel, nicolas.ferre, boris.brezillon, alexandre.belloni,
	robh+dt, broonie
  Cc: Cristian.Birsan, Cyrille Pitchen

From: Cyrille Pitchen <cyrille.pitchen@atmel.com>

This patch sets the pin muxing for the I2S controllers

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
[codrin.ciubotariu@microchip.com: added pin muxing for the second
controller]
Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
---
 arch/arm/boot/dts/at91-sama5d2_xplained.dts | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained.dts b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
index e4bbb7e..0391731 100644
--- a/arch/arm/boot/dts/at91-sama5d2_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
@@ -281,6 +281,11 @@
 				status = "okay";
 			};
 
+			i2s0: i2s@f8050000 {
+				pinctrl-names = "default";
+				pinctrl-0 = <&pinctrl_i2s0_default>;
+			};
+
 			can0: can@f8054000 {
 				pinctrl-names = "default";
 				pinctrl-0 = <&pinctrl_can0_default>;
@@ -424,6 +429,24 @@
 					bias-disable;
 				};
 
+				pinctrl_i2s0_default: i2s0_default {
+					pinmux = <PIN_PC1__I2SC0_CK>,
+						 <PIN_PC2__I2SC0_MCK>,
+						 <PIN_PC3__I2SC0_WS>,
+						 <PIN_PC4__I2SC0_DI0>,
+						 <PIN_PC5__I2SC0_DO0>;
+					bias-disable;
+				};
+
+				pinctrl_i2s1_default: i2s1_default {
+					pinmux = <PIN_PA15__I2SC1_CK>,
+						 <PIN_PA14__I2SC1_MCK>,
+						 <PIN_PA16__I2SC1_WS>,
+						 <PIN_PA17__I2SC1_DI0>,
+						 <PIN_PA18__I2SC1_DO0>;
+					bias-disable;
+				};
+
 				pinctrl_key_gpio_default: key_gpio_default {
 					pinmux = <PIN_PB9__GPIO>;
 					bias-pull-up;
@@ -546,6 +569,11 @@
 				status = "okay";
 			};
 
+			i2s1: i2s@fc04c000 {
+				pinctrl-names = "default";
+				pinctrl-0 = <&pinctrl_i2s1_default>;
+			};
+
 			can1: can@fc050000 {
 				pinctrl-names = "default";
 				pinctrl-0 = <&pinctrl_can1_default>;
-- 
2.7.4

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

* Applied "ASoC: atmel-i2s: add driver for the new Atmel I2S controller" to the asoc tree
  2018-05-25 12:34 ` [PATCH v4 5/7] ASoC: atmel-i2s: add driver for the new Atmel I2S controller Codrin Ciubotariu
@ 2018-05-29 14:59   ` Mark Brown
  0 siblings, 0 replies; 21+ messages in thread
From: Mark Brown @ 2018-05-29 14:59 UTC (permalink / raw)
  To: Cyrille Pitchen
  Cc: Codrin Ciubotariu, Mark Brown, linux-arm-kernel, linux-kernel,
	devicetree, linux-clk, alsa-devel, nicolas.ferre,
	boris.brezillon, alexandre.belloni, robh+dt, broonie,
	Cristian.Birsan, alsa-devel

The patch

   ASoC: atmel-i2s: add driver for the new Atmel I2S controller

has been applied to the asoc tree at

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

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

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

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

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

Thanks,
Mark

>From b543e467d1a9451013cd89ddcacde07dda9e7f32 Mon Sep 17 00:00:00 2001
From: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Date: Fri, 25 May 2018 15:34:26 +0300
Subject: [PATCH] ASoC: atmel-i2s: add driver for the new Atmel I2S controller

This patch adds support for the Atmel I2S controller embedded into
sama5d2x SoCs.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 sound/soc/atmel/Kconfig     |   9 +
 sound/soc/atmel/Makefile    |   2 +
 sound/soc/atmel/atmel-i2s.c | 765 ++++++++++++++++++++++++++++++++++++
 3 files changed, 776 insertions(+)
 create mode 100644 sound/soc/atmel/atmel-i2s.c

diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index dcee145dd179..64b784e96f84 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -88,4 +88,13 @@ config SND_ATMEL_SOC_TSE850_PCM5142
 	help
 	  Say Y if you want to add support for the ASoC driver for the
 	  Axentia TSE-850 with a PCM5142 codec.
+
+config SND_ATMEL_SOC_I2S
+	tristate "Atmel ASoC driver for boards using I2S"
+	depends on OF && (ARCH_AT91 || COMPILE_TEST)
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	select REGMAP_MMIO
+	help
+	  Say Y or M if you want to add support for Atmel ASoc driver for boards
+	  using I2S.
 endif
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index 4440646416e8..cd87cb4bcff5 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -3,10 +3,12 @@
 snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
 snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
 snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
+snd-soc-atmel-i2s-objs := atmel-i2s.o
 
 obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
 obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
 obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
+obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o
 
 # AT91 Machine Support
 snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
diff --git a/sound/soc/atmel/atmel-i2s.c b/sound/soc/atmel/atmel-i2s.c
new file mode 100644
index 000000000000..5d3b5af9fd92
--- /dev/null
+++ b/sound/soc/atmel/atmel-i2s.c
@@ -0,0 +1,765 @@
+/*
+ * Driver for Atmel I2S controller
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ *
+ * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#define ATMEL_I2SC_MAX_TDM_CHANNELS	8
+
+/*
+ * ---- I2S Controller Register map ----
+ */
+#define ATMEL_I2SC_CR		0x0000	/* Control Register */
+#define ATMEL_I2SC_MR		0x0004	/* Mode Register */
+#define ATMEL_I2SC_SR		0x0008	/* Status Register */
+#define ATMEL_I2SC_SCR		0x000c	/* Status Clear Register */
+#define ATMEL_I2SC_SSR		0x0010	/* Status Set Register */
+#define ATMEL_I2SC_IER		0x0014	/* Interrupt Enable Register */
+#define ATMEL_I2SC_IDR		0x0018	/* Interrupt Disable Register */
+#define ATMEL_I2SC_IMR		0x001c	/* Interrupt Mask Register */
+#define ATMEL_I2SC_RHR		0x0020	/* Receiver Holding Register */
+#define ATMEL_I2SC_THR		0x0024	/* Transmitter Holding Register */
+#define ATMEL_I2SC_VERSION	0x0028	/* Version Register */
+
+/*
+ * ---- Control Register (Write-only) ----
+ */
+#define ATMEL_I2SC_CR_RXEN	BIT(0)	/* Receiver Enable */
+#define ATMEL_I2SC_CR_RXDIS	BIT(1)	/* Receiver Disable */
+#define ATMEL_I2SC_CR_CKEN	BIT(2)	/* Clock Enable */
+#define ATMEL_I2SC_CR_CKDIS	BIT(3)	/* Clock Disable */
+#define ATMEL_I2SC_CR_TXEN	BIT(4)	/* Transmitter Enable */
+#define ATMEL_I2SC_CR_TXDIS	BIT(5)	/* Transmitter Disable */
+#define ATMEL_I2SC_CR_SWRST	BIT(7)	/* Software Reset */
+
+/*
+ * ---- Mode Register (Read/Write) ----
+ */
+#define ATMEL_I2SC_MR_MODE_MASK		GENMASK(0, 0)
+#define ATMEL_I2SC_MR_MODE_SLAVE	(0 << 0)
+#define ATMEL_I2SC_MR_MODE_MASTER	(1 << 0)
+
+#define ATMEL_I2SC_MR_DATALENGTH_MASK		GENMASK(4, 2)
+#define ATMEL_I2SC_MR_DATALENGTH_32_BITS	(0 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_24_BITS	(1 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_20_BITS	(2 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_18_BITS	(3 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_16_BITS	(4 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_16_BITS_COMPACT	(5 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_8_BITS		(6 << 2)
+#define ATMEL_I2SC_MR_DATALENGTH_8_BITS_COMPACT	(7 << 2)
+
+#define ATMEL_I2SC_MR_FORMAT_MASK	GENMASK(7, 6)
+#define ATMEL_I2SC_MR_FORMAT_I2S	(0 << 6)
+#define ATMEL_I2SC_MR_FORMAT_LJ		(1 << 6)  /* Left Justified */
+#define ATMEL_I2SC_MR_FORMAT_TDM	(2 << 6)
+#define ATMEL_I2SC_MR_FORMAT_TDMLJ	(3 << 6)
+
+/* Left audio samples duplicated to right audio channel */
+#define ATMEL_I2SC_MR_RXMONO		BIT(8)
+
+/* Receiver uses one DMA channel ... */
+#define ATMEL_I2SC_MR_RXDMA_MASK	GENMASK(9, 9)
+#define ATMEL_I2SC_MR_RXDMA_SINGLE	(0 << 9)  /* for all audio channels */
+#define ATMEL_I2SC_MR_RXDMA_MULTIPLE	(1 << 9)  /* per audio channel */
+
+/* I2SDO output of I2SC is internally connected to I2SDI input */
+#define ATMEL_I2SC_MR_RXLOOP		BIT(10)
+
+/* Left audio samples duplicated to right audio channel */
+#define ATMEL_I2SC_MR_TXMONO		BIT(12)
+
+/* Transmitter uses one DMA channel ... */
+#define ATMEL_I2SC_MR_TXDMA_MASK	GENMASK(13, 13)
+#define ATMEL_I2SC_MR_TXDMA_SINGLE	(0 << 13)  /* for all audio channels */
+#define ATMEL_I2SC_MR_TXDME_MULTIPLE	(1 << 13)  /* per audio channel */
+
+/* x sample transmitted when underrun */
+#define ATMEL_I2SC_MR_TXSAME_MASK	GENMASK(14, 14)
+#define ATMEL_I2SC_MR_TXSAME_ZERO	(0 << 14)  /* Zero sample */
+#define ATMEL_I2SC_MR_TXSAME_PREVIOUS	(1 << 14)  /* Previous sample */
+
+/* Audio Clock to I2SC Master Clock ratio */
+#define ATMEL_I2SC_MR_IMCKDIV_MASK	GENMASK(21, 16)
+#define ATMEL_I2SC_MR_IMCKDIV(div) \
+	(((div) << 16) & ATMEL_I2SC_MR_IMCKDIV_MASK)
+
+/* Master Clock to fs ratio */
+#define ATMEL_I2SC_MR_IMCKFS_MASK	GENMASK(29, 24)
+#define ATMEL_I2SC_MR_IMCKFS(fs) \
+	(((fs) << 24) & ATMEL_I2SC_MR_IMCKFS_MASK)
+
+/* Master Clock mode */
+#define ATMEL_I2SC_MR_IMCKMODE_MASK	GENMASK(30, 30)
+/* 0: No master clock generated (selected clock drives I2SCK pin) */
+#define ATMEL_I2SC_MR_IMCKMODE_I2SCK	(0 << 30)
+/* 1: master clock generated (internally generated clock drives I2SMCK pin) */
+#define ATMEL_I2SC_MR_IMCKMODE_I2SMCK	(1 << 30)
+
+/* Slot Width */
+/* 0: slot is 32 bits wide for DATALENGTH = 18/20/24 bits. */
+/* 1: slot is 24 bits wide for DATALENGTH = 18/20/24 bits. */
+#define ATMEL_I2SC_MR_IWS		BIT(31)
+
+/*
+ * ---- Status Registers ----
+ */
+#define ATMEL_I2SC_SR_RXEN	BIT(0)	/* Receiver Enabled */
+#define ATMEL_I2SC_SR_RXRDY	BIT(1)	/* Receive Ready */
+#define ATMEL_I2SC_SR_RXOR	BIT(2)	/* Receive Overrun */
+
+#define ATMEL_I2SC_SR_TXEN	BIT(4)	/* Transmitter Enabled */
+#define ATMEL_I2SC_SR_TXRDY	BIT(5)	/* Transmit Ready */
+#define ATMEL_I2SC_SR_TXUR	BIT(6)	/* Transmit Underrun */
+
+/* Receive Overrun Channel */
+#define ATMEL_I2SC_SR_RXORCH_MASK	GENMASK(15, 8)
+#define ATMEL_I2SC_SR_RXORCH(ch)	(1 << (((ch) & 0x7) + 8))
+
+/* Transmit Underrun Channel */
+#define ATMEL_I2SC_SR_TXURCH_MASK	GENMASK(27, 20)
+#define ATMEL_I2SC_SR_TXURCH(ch)	(1 << (((ch) & 0x7) + 20))
+
+/*
+ * ---- Interrupt Enable/Disable/Mask Registers ----
+ */
+#define ATMEL_I2SC_INT_RXRDY	ATMEL_I2SC_SR_RXRDY
+#define ATMEL_I2SC_INT_RXOR	ATMEL_I2SC_SR_RXOR
+#define ATMEL_I2SC_INT_TXRDY	ATMEL_I2SC_SR_TXRDY
+#define ATMEL_I2SC_INT_TXUR	ATMEL_I2SC_SR_TXUR
+
+static const struct regmap_config atmel_i2s_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = ATMEL_I2SC_VERSION,
+};
+
+struct atmel_i2s_gck_param {
+	int		fs;
+	unsigned long	mck;
+	int		imckdiv;
+	int		imckfs;
+};
+
+#define I2S_MCK_12M288		12288000UL
+#define I2S_MCK_11M2896		11289600UL
+
+/* mck = (32 * (imckfs+1) / (imckdiv+1)) * fs */
+static const struct atmel_i2s_gck_param gck_params[] = {
+	/* mck = 12.288MHz */
+	{  8000, I2S_MCK_12M288, 0, 47},	/* mck = 1536 fs */
+	{ 16000, I2S_MCK_12M288, 1, 47},	/* mck =  768 fs */
+	{ 24000, I2S_MCK_12M288, 3, 63},	/* mck =  512 fs */
+	{ 32000, I2S_MCK_12M288, 3, 47},	/* mck =  384 fs */
+	{ 48000, I2S_MCK_12M288, 7, 63},	/* mck =  256 fs */
+	{ 64000, I2S_MCK_12M288, 7, 47},	/* mck =  192 fs */
+	{ 96000, I2S_MCK_12M288, 7, 31},	/* mck =  128 fs */
+	{192000, I2S_MCK_12M288, 7, 15},	/* mck =   64 fs */
+
+	/* mck = 11.2896MHz */
+	{ 11025, I2S_MCK_11M2896, 1, 63},	/* mck = 1024 fs */
+	{ 22050, I2S_MCK_11M2896, 3, 63},	/* mck =  512 fs */
+	{ 44100, I2S_MCK_11M2896, 7, 63},	/* mck =  256 fs */
+	{ 88200, I2S_MCK_11M2896, 7, 31},	/* mck =  128 fs */
+	{176400, I2S_MCK_11M2896, 7, 15},	/* mck =   64 fs */
+};
+
+struct atmel_i2s_dev;
+
+struct atmel_i2s_caps {
+	int	(*mck_init)(struct atmel_i2s_dev *, struct device_node *np);
+};
+
+struct atmel_i2s_dev {
+	struct device				*dev;
+	struct regmap				*regmap;
+	struct clk				*pclk;
+	struct clk				*gclk;
+	struct clk				*aclk;
+	struct snd_dmaengine_dai_dma_data	playback;
+	struct snd_dmaengine_dai_dma_data	capture;
+	unsigned int				fmt;
+	const struct atmel_i2s_gck_param	*gck_param;
+	const struct atmel_i2s_caps		*caps;
+};
+
+static irqreturn_t atmel_i2s_interrupt(int irq, void *dev_id)
+{
+	struct atmel_i2s_dev *dev = dev_id;
+	unsigned int sr, imr, pending, ch, mask;
+	irqreturn_t ret = IRQ_NONE;
+
+	regmap_read(dev->regmap, ATMEL_I2SC_SR, &sr);
+	regmap_read(dev->regmap, ATMEL_I2SC_IMR, &imr);
+	pending = sr & imr;
+
+	if (!pending)
+		return IRQ_NONE;
+
+	if (pending & ATMEL_I2SC_INT_RXOR) {
+		mask = ATMEL_I2SC_SR_RXOR;
+
+		for (ch = 0; ch < ATMEL_I2SC_MAX_TDM_CHANNELS; ++ch) {
+			if (sr & ATMEL_I2SC_SR_RXORCH(ch)) {
+				mask |= ATMEL_I2SC_SR_RXORCH(ch);
+				dev_err(dev->dev,
+					"RX overrun on channel %d\n", ch);
+			}
+		}
+		regmap_write(dev->regmap, ATMEL_I2SC_SCR, mask);
+		ret = IRQ_HANDLED;
+	}
+
+	if (pending & ATMEL_I2SC_INT_TXUR) {
+		mask = ATMEL_I2SC_SR_TXUR;
+
+		for (ch = 0; ch < ATMEL_I2SC_MAX_TDM_CHANNELS; ++ch) {
+			if (sr & ATMEL_I2SC_SR_TXURCH(ch)) {
+				mask |= ATMEL_I2SC_SR_TXURCH(ch);
+				dev_err(dev->dev,
+					"TX underrun on channel %d\n", ch);
+			}
+		}
+		regmap_write(dev->regmap, ATMEL_I2SC_SCR, mask);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+#define ATMEL_I2S_RATES		SNDRV_PCM_RATE_8000_192000
+
+#define ATMEL_I2S_FORMATS	(SNDRV_PCM_FMTBIT_S8 |		\
+				 SNDRV_PCM_FMTBIT_S16_LE |	\
+				 SNDRV_PCM_FMTBIT_S18_3LE |	\
+				 SNDRV_PCM_FMTBIT_S20_3LE |	\
+				 SNDRV_PCM_FMTBIT_S24_3LE |	\
+				 SNDRV_PCM_FMTBIT_S24_LE |	\
+				 SNDRV_PCM_FMTBIT_S32_LE)
+
+static int atmel_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+	dev->fmt = fmt;
+	return 0;
+}
+
+static int atmel_i2s_prepare(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+	bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	unsigned int rhr, sr = 0;
+
+	if (is_playback) {
+		regmap_read(dev->regmap, ATMEL_I2SC_SR, &sr);
+		if (sr & ATMEL_I2SC_SR_RXRDY) {
+			/*
+			 * The RX Ready flag should not be set. However if here,
+			 * we flush (read) the Receive Holding Register to start
+			 * from a clean state.
+			 */
+			dev_dbg(dev->dev, "RXRDY is set\n");
+			regmap_read(dev->regmap, ATMEL_I2SC_RHR, &rhr);
+		}
+	}
+
+	return 0;
+}
+
+static int atmel_i2s_get_gck_param(struct atmel_i2s_dev *dev, int fs)
+{
+	int i, best;
+
+	if (!dev->gclk || !dev->aclk) {
+		dev_err(dev->dev, "cannot generate the I2S Master Clock\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Find the best possible settings to generate the I2S Master Clock
+	 * from the PLL Audio.
+	 */
+	dev->gck_param = NULL;
+	best = INT_MAX;
+	for (i = 0; i < ARRAY_SIZE(gck_params); ++i) {
+		const struct atmel_i2s_gck_param *gck_param = &gck_params[i];
+		int val = abs(fs - gck_param->fs);
+
+		if (val < best) {
+			best = val;
+			dev->gck_param = gck_param;
+		}
+	}
+
+	return 0;
+}
+
+static int atmel_i2s_hw_params(struct snd_pcm_substream *substream,
+			       struct snd_pcm_hw_params *params,
+			       struct snd_soc_dai *dai)
+{
+	struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+	bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	unsigned int mr = 0;
+	int ret;
+
+	switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		mr |= ATMEL_I2SC_MR_FORMAT_I2S;
+		break;
+
+	default:
+		dev_err(dev->dev, "unsupported bus format\n");
+		return -EINVAL;
+	}
+
+	switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		/* codec is slave, so cpu is master */
+		mr |= ATMEL_I2SC_MR_MODE_MASTER;
+		ret = atmel_i2s_get_gck_param(dev, params_rate(params));
+		if (ret)
+			return ret;
+		break;
+
+	case SND_SOC_DAIFMT_CBM_CFM:
+		/* codec is master, so cpu is slave */
+		mr |= ATMEL_I2SC_MR_MODE_SLAVE;
+		dev->gck_param = NULL;
+		break;
+
+	default:
+		dev_err(dev->dev, "unsupported master/slave mode\n");
+		return -EINVAL;
+	}
+
+	switch (params_channels(params)) {
+	case 1:
+		if (is_playback)
+			mr |= ATMEL_I2SC_MR_TXMONO;
+		else
+			mr |= ATMEL_I2SC_MR_RXMONO;
+		break;
+	case 2:
+		break;
+	default:
+		dev_err(dev->dev, "unsupported number of audio channels\n");
+		return -EINVAL;
+	}
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_8_BITS;
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_LE:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_16_BITS;
+		break;
+
+	case SNDRV_PCM_FORMAT_S18_3LE:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_18_BITS | ATMEL_I2SC_MR_IWS;
+		break;
+
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_20_BITS | ATMEL_I2SC_MR_IWS;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_24_BITS | ATMEL_I2SC_MR_IWS;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_24_BITS;
+		break;
+
+	case SNDRV_PCM_FORMAT_S32_LE:
+		mr |= ATMEL_I2SC_MR_DATALENGTH_32_BITS;
+		break;
+
+	default:
+		dev_err(dev->dev, "unsupported size/endianness for audio samples\n");
+		return -EINVAL;
+	}
+
+	return regmap_write(dev->regmap, ATMEL_I2SC_MR, mr);
+}
+
+static int atmel_i2s_switch_mck_generator(struct atmel_i2s_dev *dev,
+					  bool enabled)
+{
+	unsigned int mr, mr_mask;
+	unsigned long aclk_rate;
+	int ret;
+
+	mr = 0;
+	mr_mask = (ATMEL_I2SC_MR_IMCKDIV_MASK |
+		   ATMEL_I2SC_MR_IMCKFS_MASK |
+		   ATMEL_I2SC_MR_IMCKMODE_MASK);
+
+	if (!enabled) {
+		/* Disable the I2S Master Clock generator. */
+		ret = regmap_write(dev->regmap, ATMEL_I2SC_CR,
+				   ATMEL_I2SC_CR_CKDIS);
+		if (ret)
+			return ret;
+
+		/* Reset the I2S Master Clock generator settings. */
+		ret = regmap_update_bits(dev->regmap, ATMEL_I2SC_MR,
+					 mr_mask, mr);
+		if (ret)
+			return ret;
+
+		/* Disable/unprepare the PMC generated clock. */
+		clk_disable_unprepare(dev->gclk);
+
+		/* Disable/unprepare the PLL audio clock. */
+		clk_disable_unprepare(dev->aclk);
+		return 0;
+	}
+
+	if (!dev->gck_param)
+		return -EINVAL;
+
+	aclk_rate = dev->gck_param->mck * (dev->gck_param->imckdiv + 1);
+
+	/* Fist change the PLL audio clock frequency ... */
+	ret = clk_set_rate(dev->aclk, aclk_rate);
+	if (ret)
+		return ret;
+
+	/*
+	 * ... then set the PMC generated clock rate to the very same frequency
+	 * to set the gclk parent to aclk.
+	 */
+	ret = clk_set_rate(dev->gclk, aclk_rate);
+	if (ret)
+		return ret;
+
+	/* Prepare and enable the PLL audio clock first ... */
+	ret = clk_prepare_enable(dev->aclk);
+	if (ret)
+		return ret;
+
+	/* ... then prepare and enable the PMC generated clock. */
+	ret = clk_prepare_enable(dev->gclk);
+	if (ret)
+		return ret;
+
+	/* Update the Mode Register to generate the I2S Master Clock. */
+	mr |= ATMEL_I2SC_MR_IMCKDIV(dev->gck_param->imckdiv);
+	mr |= ATMEL_I2SC_MR_IMCKFS(dev->gck_param->imckfs);
+	mr |= ATMEL_I2SC_MR_IMCKMODE_I2SMCK;
+	ret = regmap_update_bits(dev->regmap, ATMEL_I2SC_MR, mr_mask, mr);
+	if (ret)
+		return ret;
+
+	/* Finally enable the I2S Master Clock generator. */
+	return regmap_write(dev->regmap, ATMEL_I2SC_CR,
+			    ATMEL_I2SC_CR_CKEN);
+}
+
+static int atmel_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+			     struct snd_soc_dai *dai)
+{
+	struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+	bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	bool is_master, mck_enabled;
+	unsigned int cr, mr;
+	int err;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		cr = is_playback ? ATMEL_I2SC_CR_TXEN : ATMEL_I2SC_CR_RXEN;
+		mck_enabled = true;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		cr = is_playback ? ATMEL_I2SC_CR_TXDIS : ATMEL_I2SC_CR_RXDIS;
+		mck_enabled = false;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Read the Mode Register to retrieve the master/slave state. */
+	err = regmap_read(dev->regmap, ATMEL_I2SC_MR, &mr);
+	if (err)
+		return err;
+	is_master = (mr & ATMEL_I2SC_MR_MODE_MASK) == ATMEL_I2SC_MR_MODE_MASTER;
+
+	/* If master starts, enable the audio clock. */
+	if (is_master && mck_enabled)
+		err = atmel_i2s_switch_mck_generator(dev, true);
+	if (err)
+		return err;
+
+	err = regmap_write(dev->regmap, ATMEL_I2SC_CR, cr);
+	if (err)
+		return err;
+
+	/* If master stops, disable the audio clock. */
+	if (is_master && !mck_enabled)
+		err = atmel_i2s_switch_mck_generator(dev, false);
+
+	return err;
+}
+
+static const struct snd_soc_dai_ops atmel_i2s_dai_ops = {
+	.prepare	= atmel_i2s_prepare,
+	.trigger	= atmel_i2s_trigger,
+	.hw_params	= atmel_i2s_hw_params,
+	.set_fmt	= atmel_i2s_set_dai_fmt,
+};
+
+static int atmel_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+	struct atmel_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+	snd_soc_dai_init_dma_data(dai, &dev->playback, &dev->capture);
+	return 0;
+}
+
+static struct snd_soc_dai_driver atmel_i2s_dai = {
+	.probe	= atmel_i2s_dai_probe,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = ATMEL_I2S_RATES,
+		.formats = ATMEL_I2S_FORMATS,
+	},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = ATMEL_I2S_RATES,
+		.formats = ATMEL_I2S_FORMATS,
+	},
+	.ops = &atmel_i2s_dai_ops,
+	.symmetric_rates = 1,
+};
+
+static const struct snd_soc_component_driver atmel_i2s_component = {
+	.name	= "atmel-i2s",
+};
+
+static int atmel_i2s_sama5d2_mck_init(struct atmel_i2s_dev *dev,
+				      struct device_node *np)
+{
+	struct clk *muxclk;
+	int err;
+
+	if (!dev->gclk)
+		return 0;
+
+	/* muxclk is optional, so we return error for probe defer only */
+	muxclk = devm_clk_get(dev->dev, "muxclk");
+	if (IS_ERR(muxclk)) {
+		err = PTR_ERR(muxclk);
+		if (err == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_warn(dev->dev,
+			 "failed to get the I2S clock control: %d\n", err);
+		return 0;
+	}
+
+	return clk_set_parent(muxclk, dev->gclk);
+}
+
+static const struct atmel_i2s_caps atmel_i2s_sama5d2_caps = {
+	.mck_init = atmel_i2s_sama5d2_mck_init,
+};
+
+static const struct of_device_id atmel_i2s_dt_ids[] = {
+	{
+		.compatible = "atmel,sama5d2-i2s",
+		.data = (void *)&atmel_i2s_sama5d2_caps,
+	},
+
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_i2s_dt_ids);
+
+static int atmel_i2s_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	const struct of_device_id *match;
+	struct atmel_i2s_dev *dev;
+	struct resource *mem;
+	struct regmap *regmap;
+	void __iomem *base;
+	int irq;
+	int err = -ENXIO;
+	unsigned int pcm_flags = 0;
+	unsigned int version;
+
+	/* Get memory for driver data. */
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	/* Get hardware capabilities. */
+	match = of_match_node(atmel_i2s_dt_ids, np);
+	if (match)
+		dev->caps = match->data;
+
+	/* Map I/O registers. */
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, mem);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	regmap = devm_regmap_init_mmio(&pdev->dev, base,
+				       &atmel_i2s_regmap_config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	/* Request IRQ. */
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	err = devm_request_irq(&pdev->dev, irq, atmel_i2s_interrupt, 0,
+			       dev_name(&pdev->dev), dev);
+	if (err)
+		return err;
+
+	/* Get the peripheral clock. */
+	dev->pclk = devm_clk_get(&pdev->dev, "pclk");
+	if (IS_ERR(dev->pclk)) {
+		err = PTR_ERR(dev->pclk);
+		dev_err(&pdev->dev,
+			"failed to get the peripheral clock: %d\n", err);
+		return err;
+	}
+
+	/* Get audio clocks to generate the I2S Master Clock (I2S_MCK) */
+	dev->aclk = devm_clk_get(&pdev->dev, "aclk");
+	dev->gclk = devm_clk_get(&pdev->dev, "gclk");
+	if (IS_ERR(dev->aclk) && IS_ERR(dev->gclk)) {
+		if (PTR_ERR(dev->aclk) == -EPROBE_DEFER ||
+		    PTR_ERR(dev->gclk) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		/* Master Mode not supported */
+		dev->aclk = NULL;
+		dev->gclk = NULL;
+	} else if (IS_ERR(dev->gclk)) {
+		err = PTR_ERR(dev->gclk);
+		dev_err(&pdev->dev,
+			"failed to get the PMC generated clock: %d\n", err);
+		return err;
+	} else if (IS_ERR(dev->aclk)) {
+		err = PTR_ERR(dev->aclk);
+		dev_err(&pdev->dev,
+			"failed to get the PLL audio clock: %d\n", err);
+		return err;
+	}
+
+	dev->dev = &pdev->dev;
+	dev->regmap = regmap;
+	platform_set_drvdata(pdev, dev);
+
+	/* Do hardware specific settings to initialize I2S_MCK generator */
+	if (dev->caps && dev->caps->mck_init) {
+		err = dev->caps->mck_init(dev, np);
+		if (err)
+			return err;
+	}
+
+	/* Enable the peripheral clock. */
+	err = clk_prepare_enable(dev->pclk);
+	if (err)
+		return err;
+
+	/* Get IP version. */
+	regmap_read(dev->regmap, ATMEL_I2SC_VERSION, &version);
+	dev_info(&pdev->dev, "hw version: %#x\n", version);
+
+	/* Enable error interrupts. */
+	regmap_write(dev->regmap, ATMEL_I2SC_IER,
+		     ATMEL_I2SC_INT_RXOR | ATMEL_I2SC_INT_TXUR);
+
+	err = devm_snd_soc_register_component(&pdev->dev,
+					      &atmel_i2s_component,
+					      &atmel_i2s_dai, 1);
+	if (err) {
+		dev_err(&pdev->dev, "failed to register DAI: %d\n", err);
+		clk_disable_unprepare(dev->pclk);
+		return err;
+	}
+
+	/* Prepare DMA config. */
+	dev->playback.addr	= (dma_addr_t)mem->start + ATMEL_I2SC_THR;
+	dev->playback.maxburst	= 1;
+	dev->capture.addr	= (dma_addr_t)mem->start + ATMEL_I2SC_RHR;
+	dev->capture.maxburst	= 1;
+
+	if (of_property_match_string(np, "dma-names", "rx-tx") == 0)
+		pcm_flags |= SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX;
+	err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, pcm_flags);
+	if (err) {
+		dev_err(&pdev->dev, "failed to register PCM: %d\n", err);
+		clk_disable_unprepare(dev->pclk);
+		return err;
+	}
+
+	return 0;
+}
+
+static int atmel_i2s_remove(struct platform_device *pdev)
+{
+	struct atmel_i2s_dev *dev = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(dev->pclk);
+
+	return 0;
+}
+
+static struct platform_driver atmel_i2s_driver = {
+	.driver		= {
+		.name	= "atmel_i2s",
+		.of_match_table	= of_match_ptr(atmel_i2s_dt_ids),
+	},
+	.probe		= atmel_i2s_probe,
+	.remove		= atmel_i2s_remove,
+};
+module_platform_driver(atmel_i2s_driver);
+
+MODULE_DESCRIPTION("Atmel I2S Controller driver");
+MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.17.0

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

* Applied "ASoC: atmel-i2s: dt-bindings: add DT bindings for I2S controller" to the asoc tree
  2018-05-25 12:34 ` [PATCH v4 4/7] ASoC: atmel-i2s: dt-bindings: add DT bindings for I2S controller Codrin Ciubotariu
@ 2018-05-29 14:59   ` Mark Brown
  0 siblings, 0 replies; 21+ messages in thread
From: Mark Brown @ 2018-05-29 14:59 UTC (permalink / raw)
  To: Cyrille Pitchen
  Cc: Codrin Ciubotariu, Mark Brown, linux-arm-kernel, linux-kernel,
	devicetree, linux-clk, alsa-devel, nicolas.ferre,
	boris.brezillon, alexandre.belloni, robh+dt, broonie,
	Cristian.Birsan, alsa-devel

The patch

   ASoC: atmel-i2s: dt-bindings: add DT bindings for I2S controller

has been applied to the asoc tree at

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

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

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

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

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

Thanks,
Mark

>From 969cc0528e0efdfdeaa5bbc0ab707d8a7819a8ec Mon Sep 17 00:00:00 2001
From: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Date: Fri, 25 May 2018 15:34:25 +0300
Subject: [PATCH] ASoC: atmel-i2s: dt-bindings: add DT bindings for I2S
 controller

This patch adds DT bindings for the new Atmel I2S controller embedded
inside sama5d2x SoCs.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 .../devicetree/bindings/sound/atmel-i2s.txt   | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/atmel-i2s.txt

diff --git a/Documentation/devicetree/bindings/sound/atmel-i2s.txt b/Documentation/devicetree/bindings/sound/atmel-i2s.txt
new file mode 100644
index 000000000000..735368b8a73f
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/atmel-i2s.txt
@@ -0,0 +1,47 @@
+* Atmel I2S controller
+
+Required properties:
+- compatible:     Should be "atmel,sama5d2-i2s".
+- reg:            Should be the physical base address of the controller and the
+                  length of memory mapped region.
+- interrupts:     Should contain the interrupt for the controller.
+- dmas:           Should be one per channel name listed in the dma-names property,
+                  as described in atmel-dma.txt and dma.txt files.
+- dma-names:      Two dmas have to be defined, "tx" and "rx".
+                  This IP also supports one shared channel for both rx and tx;
+                  if this mode is used, one "rx-tx" name must be used.
+- clocks:         Must contain an entry for each entry in clock-names.
+                  Please refer to clock-bindings.txt.
+- clock-names:    Should be one of each entry matching the clocks phandles list:
+                  - "pclk" (peripheral clock) Required.
+                  - "gclk" (generated clock) Optional (1).
+                  - "aclk" (Audio PLL clock) Optional (1).
+                  - "muxclk" (I2S mux clock) Optional (1).
+
+Optional properties:
+- pinctrl-0:      Should specify pin control groups used for this controller.
+- princtrl-names: Should contain only one value - "default".
+
+
+(1) : Only the peripheral clock is required. The generated clock, the Audio
+      PLL clock adn the I2S mux clock are optional and should only be set
+      together, when Master Mode is required.
+
+Example:
+
+	i2s@f8050000 {
+		compatible = "atmel,sama5d2-i2s";
+		reg = <0xf8050000 0x300>;
+		interrupts = <54 IRQ_TYPE_LEVEL_HIGH 7>;
+		dmas = <&dma0
+			(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+			 AT91_XDMAC_DT_PERID(31))>,
+		       <&dma0
+			(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+			 AT91_XDMAC_DT_PERID(32))>;
+		dma-names = "tx", "rx";
+		clocks = <&i2s0_clk>, <&i2s0_gclk>, <&audio_pll_pmc>, <&i2s0muxck>;
+		clock-names = "pclk", "gclk", "aclk", "muxclk";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_i2s0_default>;
+	};
-- 
2.17.0

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

* Re: [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock
  2018-05-25 12:34 ` [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock Codrin Ciubotariu
@ 2018-05-31  0:58   ` Rob Herring
  2018-05-31 10:25     ` Codrin Ciubotariu
  0 siblings, 1 reply; 21+ messages in thread
From: Rob Herring @ 2018-05-31  0:58 UTC (permalink / raw)
  To: Codrin Ciubotariu
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-clk,
	alsa-devel, nicolas.ferre, boris.brezillon, alexandre.belloni,
	broonie, Cristian.Birsan

On Fri, May 25, 2018 at 03:34:22PM +0300, Codrin Ciubotariu wrote:
> The I2S mux clock can be used to select the I2S input clock. The
> available parents are the peripheral and the generated clocks.
> 
> Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
> ---
>  .../devicetree/bindings/clock/at91-clock.txt       | 34 ++++++++++++++++++++++
>  1 file changed, 34 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt
> index 51c259a..1c46b3c 100644
> --- a/Documentation/devicetree/bindings/clock/at91-clock.txt
> +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
> @@ -90,6 +90,8 @@ Required properties:
>  	"atmel,sama5d2-clk-audio-pll-pmc"
>  		at91 audio pll output on AUDIOPLLCLK that feeds the PMC
>  		and can be used by peripheral clock or generic clock
> +	"atmel,sama5d2-clk-i2s-mux":
> +		at91 I2S clock source selection

Is this boolean or takes some values. If latter, what are valid values?

>  
>  Required properties for SCKC node:
>  - reg : defines the IO memory reserved for the SCKC.
> @@ -507,3 +509,35 @@ For example:
>  			atmel,clk-output-range = <0 83000000>;
>  		};
>  	};
> +
> +Required properties for I2S mux clocks:
> +- #size-cells : shall be 0 (reg is used to encode I2S bus id).
> +- #address-cells : shall be 1 (reg is used to encode I2S bus id).
> +- name: device tree node describing a specific mux clock.
> +	* #clock-cells : from common clock binding; shall be set to 0.
> +	* clocks : shall be the mux clock parent phandles; shall be 2 phandles:
> +	  peripheral and generated clock; the first phandle shall belong to the
> +	  peripheral clock and the second one shall belong to the generated
> +	  clock; "clock-indices" property can be user to specify
> +	  the correct order.
> +	* reg: I2S bus id of the corresponding mux clock.
> +	  e.g. reg = <0>; for i2s0, reg = <1>; for i2s1
> +
> +For example:
> +	i2s_clkmux {

What is this a child of?

> +		compatible = "atmel,sama5d2-clk-i2s-mux";
> +		#address-cells = <1>;
> +		#size-cells = <0>;

How do you address this block? My guess is you don't because it is just 
part of some other block and you are just creating this node to 
instantiate a driver. Just make the node for the actual h/w block a 
clock provider and define the clock ids (0 and 1).

> +
> +		i2s0muxck: i2s0_muxclk {
> +			clocks = <&i2s0_clk>, <&i2s0_gclk>;
> +			#clock-cells = <0>;
> +			reg = <0>;
> +		};
> +
> +		i2s1muxck: i2s1_muxclk {
> +			clocks = <&i2s1_clk>, <&i2s1_gclk>;
> +			#clock-cells = <0>;
> +			reg = <1>;
> +		};
> +	};
> -- 
> 2.7.4
> 

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

* Re: [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock
  2018-05-31  0:58   ` Rob Herring
@ 2018-05-31 10:25     ` Codrin Ciubotariu
  2018-05-31 14:20       ` Rob Herring
  0 siblings, 1 reply; 21+ messages in thread
From: Codrin Ciubotariu @ 2018-05-31 10:25 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-arm-kernel, linux-kernel, devicetree, linux-clk,
	alsa-devel, nicolas.ferre, boris.brezillon, alexandre.belloni,
	broonie, Cristian.Birsan

On 31.05.2018 03:58, Rob Herring wrote:
> On Fri, May 25, 2018 at 03:34:22PM +0300, Codrin Ciubotariu wrote:
>> The I2S mux clock can be used to select the I2S input clock. The
>> available parents are the peripheral and the generated clocks.
>>
>> Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
>> ---
>>   .../devicetree/bindings/clock/at91-clock.txt       | 34 ++++++++++++++++++++++
>>   1 file changed, 34 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt
>> index 51c259a..1c46b3c 100644
>> --- a/Documentation/devicetree/bindings/clock/at91-clock.txt
>> +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
>> @@ -90,6 +90,8 @@ Required properties:
>>   	"atmel,sama5d2-clk-audio-pll-pmc"
>>   		at91 audio pll output on AUDIOPLLCLK that feeds the PMC
>>   		and can be used by peripheral clock or generic clock
>> +	"atmel,sama5d2-clk-i2s-mux":
>> +		at91 I2S clock source selection
> 
> Is this boolean or takes some values. If latter, what are valid values?

This is the compatible string of the clock driver.

> 
>>   
>>   Required properties for SCKC node:
>>   - reg : defines the IO memory reserved for the SCKC.
>> @@ -507,3 +509,35 @@ For example:
>>   			atmel,clk-output-range = <0 83000000>;
>>   		};
>>   	};
>> +
>> +Required properties for I2S mux clocks:
>> +- #size-cells : shall be 0 (reg is used to encode I2S bus id).
>> +- #address-cells : shall be 1 (reg is used to encode I2S bus id).
>> +- name: device tree node describing a specific mux clock.
>> +	* #clock-cells : from common clock binding; shall be set to 0.
>> +	* clocks : shall be the mux clock parent phandles; shall be 2 phandles:
>> +	  peripheral and generated clock; the first phandle shall belong to the
>> +	  peripheral clock and the second one shall belong to the generated
>> +	  clock; "clock-indices" property can be user to specify
>> +	  the correct order.
>> +	* reg: I2S bus id of the corresponding mux clock.
>> +	  e.g. reg = <0>; for i2s0, reg = <1>; for i2s1
>> +
>> +For example:
>> +	i2s_clkmux {
> 
> What is this a child of?

It is a child of PMC node, since both parent clocks are generated by PMC.

> 
>> +		compatible = "atmel,sama5d2-clk-i2s-mux";
>> +		#address-cells = <1>;
>> +		#size-cells = <0>;
> 
> How do you address this block? My guess is you don't because it is just
> part of some other block and you are just creating this node to
> instantiate a driver. Just make the node for the actual h/w block a
> clock provider and define the clock ids (0 and 1).

This block is not addressed, but its children are. The register we 
access in this driver is not part of other block. It's a SFR register, 
accessed through syscon and it has nothing to do with the I2S IP (see 
SAMA5D2 DS, page 1256, fig. 44-1: I2SC Block Diagram) that is the 
consumer of this clock. Adding a clock-id property in the I2S node would 
be just like v3 of this series, with the difference that we use clock-id 
instead of alias id to set the clock parent, which is not how you 
suggested back then.

Thank you for your review.

Best regards,
Codrin

[...]

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

* Re: [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock
  2018-05-31 10:25     ` Codrin Ciubotariu
@ 2018-05-31 14:20       ` Rob Herring
  2018-05-31 15:31         ` Stephen Boyd
  2018-05-31 16:01         ` Alexandre Belloni
  0 siblings, 2 replies; 21+ messages in thread
From: Rob Herring @ 2018-05-31 14:20 UTC (permalink / raw)
  To: Codrin Ciubotariu
  Cc: moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	linux-kernel, devicetree, linux-clk, Linux-ALSA, Nicolas Ferre,
	Boris Brezillon, Alexandre Belloni, Mark Brown, Cristian.Birsan

On Thu, May 31, 2018 at 5:25 AM, Codrin Ciubotariu
<codrin.ciubotariu@microchip.com> wrote:
> On 31.05.2018 03:58, Rob Herring wrote:
>>
>> On Fri, May 25, 2018 at 03:34:22PM +0300, Codrin Ciubotariu wrote:
>>>
>>> The I2S mux clock can be used to select the I2S input clock. The
>>> available parents are the peripheral and the generated clocks.
>>>
>>> Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
>>> ---
>>>   .../devicetree/bindings/clock/at91-clock.txt       | 34
>>> ++++++++++++++++++++++
>>>   1 file changed, 34 insertions(+)
>>>
>>> diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt
>>> b/Documentation/devicetree/bindings/clock/at91-clock.txt
>>> index 51c259a..1c46b3c 100644
>>> --- a/Documentation/devicetree/bindings/clock/at91-clock.txt
>>> +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
>>> @@ -90,6 +90,8 @@ Required properties:
>>>         "atmel,sama5d2-clk-audio-pll-pmc"
>>>                 at91 audio pll output on AUDIOPLLCLK that feeds the PMC
>>>                 and can be used by peripheral clock or generic clock
>>> +       "atmel,sama5d2-clk-i2s-mux":
>>> +               at91 I2S clock source selection
>>
>>
>> Is this boolean or takes some values. If latter, what are valid values?
>
>
> This is the compatible string of the clock driver.

Ah, now I remember. AT91 uses fine grained clock nodes in DT. Is there
still a plan to fix this?

>>>     Required properties for SCKC node:
>>>   - reg : defines the IO memory reserved for the SCKC.
>>> @@ -507,3 +509,35 @@ For example:
>>>                         atmel,clk-output-range = <0 83000000>;
>>>                 };
>>>         };
>>> +
>>> +Required properties for I2S mux clocks:
>>> +- #size-cells : shall be 0 (reg is used to encode I2S bus id).
>>> +- #address-cells : shall be 1 (reg is used to encode I2S bus id).
>>> +- name: device tree node describing a specific mux clock.
>>> +       * #clock-cells : from common clock binding; shall be set to 0.
>>> +       * clocks : shall be the mux clock parent phandles; shall be 2
>>> phandles:
>>> +         peripheral and generated clock; the first phandle shall belong
>>> to the
>>> +         peripheral clock and the second one shall belong to the
>>> generated
>>> +         clock; "clock-indices" property can be user to specify
>>> +         the correct order.
>>> +       * reg: I2S bus id of the corresponding mux clock.
>>> +         e.g. reg = <0>; for i2s0, reg = <1>; for i2s1
>>> +
>>> +For example:
>>> +       i2s_clkmux {
>>
>>
>> What is this a child of?
>
>
> It is a child of PMC node, since both parent clocks are generated by PMC.
>
>>
>>> +               compatible = "atmel,sama5d2-clk-i2s-mux";
>>> +               #address-cells = <1>;
>>> +               #size-cells = <0>;
>>
>>
>> How do you address this block? My guess is you don't because it is just
>> part of some other block and you are just creating this node to
>> instantiate a driver. Just make the node for the actual h/w block a
>> clock provider and define the clock ids (0 and 1).
>
>
> This block is not addressed, but its children are. The register we access in
> this driver is not part of other block. It's a SFR register, accessed
> through syscon and it has nothing to do with the I2S IP (see SAMA5D2 DS,
> page 1256, fig. 44-1: I2SC Block Diagram) that is the consumer of this
> clock. Adding a clock-id property in the I2S node would be just like v3 of
> this series, with the difference that we use clock-id instead of alias id to
> set the clock parent, which is not how you suggested back then.

I wasn't suggesting a clock-id property, but a clock specifier (i.e.
make #clock-cells 1).

But AT91 clocks are all a mess, so I don't know what to tell you.

Rob

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

* Re: [PATCH v4 2/7] clk: at91: add I2S clock mux driver
  2018-05-25 12:34 ` [PATCH v4 2/7] clk: at91: add I2S clock mux driver Codrin Ciubotariu
@ 2018-05-31 15:26   ` Stephen Boyd
  2018-06-04  8:20     ` Codrin Ciubotariu
  0 siblings, 1 reply; 21+ messages in thread
From: Stephen Boyd @ 2018-05-31 15:26 UTC (permalink / raw)
  To: Codrin Ciubotariu, alexandre.belloni, alsa-devel,
	boris.brezillon, broonie, devicetree, linux-arm-kernel,
	linux-clk, linux-kernel, nicolas.ferre, robh+dt
  Cc: Cristian.Birsan

Quoting Codrin Ciubotariu (2018-05-25 05:34:23)
> This driver is a simple muxing driver that controls the
> I2S's clock input by using syscon/regmap to change the parrent.

s/parrent/parent/

> The available inputs can be Peripheral clock and Generated clock.

Why capitalize peripheral and generated?

> 
> Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
> ---
> diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
> index 1254bf9..903f23c 100644
> --- a/arch/arm/mach-at91/Kconfig
> +++ b/arch/arm/mach-at91/Kconfig
> @@ -27,6 +27,7 @@ config SOC_SAMA5D2
>         select HAVE_AT91_H32MX
>         select HAVE_AT91_GENERATED_CLK
>         select HAVE_AT91_AUDIO_PLL
> +       select HAVE_AT91_I2S_MUX_CLK
>         select PINCTRL_AT91PIO4
>         help
>           Select this if ou are using one of Microchip's SAMA5D2 family SoC.
> @@ -129,6 +130,9 @@ config HAVE_AT91_GENERATED_CLK
>  config HAVE_AT91_AUDIO_PLL
>         bool
>  
> +config HAVE_AT91_I2S_MUX_CLK
> +       bool
> +
>  config SOC_SAM_V4_V5
>         bool
>  

I guess this is OK.

> diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
> index 082596f..facc169 100644
> --- a/drivers/clk/at91/Makefile
> +++ b/drivers/clk/at91/Makefile
> @@ -13,3 +13,4 @@ obj-$(CONFIG_HAVE_AT91_USB_CLK)               += clk-usb.o
>  obj-$(CONFIG_HAVE_AT91_SMD)            += clk-smd.o
>  obj-$(CONFIG_HAVE_AT91_H32MX)          += clk-h32mx.o
>  obj-$(CONFIG_HAVE_AT91_GENERATED_CLK)  += clk-generated.o
> +obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK)    += clk-i2s-mux.o
> diff --git a/drivers/clk/at91/clk-i2s-mux.c b/drivers/clk/at91/clk-i2s-mux.c
> new file mode 100644
> index 0000000..2d56ded
> --- /dev/null
> +++ b/drivers/clk/at91/clk-i2s-mux.c
> @@ -0,0 +1,117 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + *  Copyright (C) 2018 Microchip Technology Inc,
> + *                     Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
> + *
> + *
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <soc/at91/atmel-sfr.h>
> +
> +#define        I2S_BUS_NR      2
> +
> +struct clk_i2s_mux {
> +       struct clk_hw hw;
> +       struct regmap *regmap;
> +       u32 bus_id;

Can be a u8?

> +};
> +
> +#define to_clk_i2s_mux(hw) container_of(hw, struct clk_i2s_mux, hw)
> +
> +static u8 clk_i2s_mux_get_parent(struct clk_hw *hw)
> +{
> +       struct clk_i2s_mux *mux = to_clk_i2s_mux(hw);
> +       u32 val;
> +
> +       regmap_read(mux->regmap, AT91_SFR_I2SCLKSEL, &val);
> +
> +       return (val & BIT(mux->bus_id)) >> mux->bus_id;
> +}
> +
> +static int clk_i2s_mux_set_parent(struct clk_hw *hw, u8 index)
> +{
> +       struct clk_i2s_mux *mux = to_clk_i2s_mux(hw);
> +
> +       return regmap_update_bits(mux->regmap, AT91_SFR_I2SCLKSEL,
> +                                 BIT(mux->bus_id), index << mux->bus_id);
> +}
> +
> +const struct clk_ops clk_i2s_mux_ops = {

static?

> +       .get_parent = clk_i2s_mux_get_parent,
> +       .set_parent = clk_i2s_mux_set_parent,
> +       .determine_rate = __clk_mux_determine_rate,
> +};
> +
> +static struct clk_hw * __init
> +at91_clk_i2s_mux_register(struct regmap *regmap, const char *name,
> +                         const char * const *parent_names,
> +                         unsigned int num_parents, u32 bus_id)
> +{
> +       struct clk_init_data init = {};
> +       struct clk_i2s_mux *i2s_ck;
> +       int ret;
> +
> +       i2s_ck = kzalloc(sizeof(*i2s_ck), GFP_KERNEL);
> +       if (!i2s_ck)
> +               return ERR_PTR(-ENOMEM);
> +
> +       init.name = name;
> +       init.ops = &clk_i2s_mux_ops;
> +       init.parent_names = parent_names;
> +       init.num_parents = num_parents;
> +       init.flags = CLK_IGNORE_UNUSED;

Really? Why?

> +
> +       i2s_ck->hw.init = &init;
> +       i2s_ck->bus_id = bus_id;
> +       i2s_ck->regmap = regmap;
> +
> +       ret = clk_hw_register(NULL, &i2s_ck->hw);
> +       if (ret) {
> +               kfree(i2s_ck);
> +               return ERR_PTR(ret);
> +       }
> +
> +       return &i2s_ck->hw;
> +}

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

* Re: [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock
  2018-05-31 14:20       ` Rob Herring
@ 2018-05-31 15:31         ` Stephen Boyd
  2018-05-31 15:56           ` Rob Herring
  2018-05-31 16:01         ` Alexandre Belloni
  1 sibling, 1 reply; 21+ messages in thread
From: Stephen Boyd @ 2018-05-31 15:31 UTC (permalink / raw)
  To: Codrin Ciubotariu, Rob Herring
  Cc: devicetree, Linux-ALSA, Alexandre Belloni, linux-kernel,
	Boris Brezillon, Mark Brown, Cristian.Birsan, linux-clk,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE

Quoting Rob Herring (2018-05-31 07:20:57)
> On Thu, May 31, 2018 at 5:25 AM, Codrin Ciubotariu
> <codrin.ciubotariu@microchip.com> wrote:
> > On 31.05.2018 03:58, Rob Herring wrote:
> >>
> >> On Fri, May 25, 2018 at 03:34:22PM +0300, Codrin Ciubotariu wrote:
> >>>
> >>> The I2S mux clock can be used to select the I2S input clock. The
> >>> available parents are the peripheral and the generated clocks.
> >>>
> >>> Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
> >>> ---
> >>>   .../devicetree/bindings/clock/at91-clock.txt       | 34
> >>> ++++++++++++++++++++++
> >>>   1 file changed, 34 insertions(+)
> >>>
> >>> diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt
> >>> b/Documentation/devicetree/bindings/clock/at91-clock.txt
> >>> index 51c259a..1c46b3c 100644
> >>> --- a/Documentation/devicetree/bindings/clock/at91-clock.txt
> >>> +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
> >>> @@ -90,6 +90,8 @@ Required properties:
> >>>         "atmel,sama5d2-clk-audio-pll-pmc"
> >>>                 at91 audio pll output on AUDIOPLLCLK that feeds the PMC
> >>>                 and can be used by peripheral clock or generic clock
> >>> +       "atmel,sama5d2-clk-i2s-mux":
> >>> +               at91 I2S clock source selection
> >>
> >>
> >> Is this boolean or takes some values. If latter, what are valid values?
> >
> >
> > This is the compatible string of the clock driver.
> 
> Ah, now I remember. AT91 uses fine grained clock nodes in DT. Is there
> still a plan to fix this?

I'm also interested in a plan.

> >>
> >>> +               compatible = "atmel,sama5d2-clk-i2s-mux";
> >>> +               #address-cells = <1>;
> >>> +               #size-cells = <0>;
> >>
> >>
> >> How do you address this block? My guess is you don't because it is just
> >> part of some other block and you are just creating this node to
> >> instantiate a driver. Just make the node for the actual h/w block a
> >> clock provider and define the clock ids (0 and 1).
> >
> >
> > This block is not addressed, but its children are. The register we access in
> > this driver is not part of other block. It's a SFR register, accessed
> > through syscon and it has nothing to do with the I2S IP (see SAMA5D2 DS,
> > page 1256, fig. 44-1: I2SC Block Diagram) that is the consumer of this
> > clock. Adding a clock-id property in the I2S node would be just like v3 of
> > this series, with the difference that we use clock-id instead of alias id to
> > set the clock parent, which is not how you suggested back then.
> 
> I wasn't suggesting a clock-id property, but a clock specifier (i.e.
> make #clock-cells 1).
> 
> But AT91 clocks are all a mess, so I don't know what to tell you.
> 

If #clock-cells of 1 works then we should go with that. It's still weird
that we need random nodes to add more clks, but I guess that's how it's
going to be for each at91 clk driver until it changes to be one big
provider node.

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

* Re: [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock
  2018-05-31 15:31         ` Stephen Boyd
@ 2018-05-31 15:56           ` Rob Herring
  2018-06-07 10:30             ` Codrin Ciubotariu
  0 siblings, 1 reply; 21+ messages in thread
From: Rob Herring @ 2018-05-31 15:56 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Codrin Ciubotariu, devicetree, Linux-ALSA, Alexandre Belloni,
	linux-kernel, Boris Brezillon, Mark Brown, Cristian.Birsan,
	linux-clk,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE

On Thu, May 31, 2018 at 10:31 AM, Stephen Boyd <sboyd@kernel.org> wrote:
> Quoting Rob Herring (2018-05-31 07:20:57)
>> On Thu, May 31, 2018 at 5:25 AM, Codrin Ciubotariu
>> <codrin.ciubotariu@microchip.com> wrote:
>> > On 31.05.2018 03:58, Rob Herring wrote:
>> >>
>> >> On Fri, May 25, 2018 at 03:34:22PM +0300, Codrin Ciubotariu wrote:
>> >>>
>> >>> The I2S mux clock can be used to select the I2S input clock. The
>> >>> available parents are the peripheral and the generated clocks.
>> >>>
>> >>> Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
>> >>> ---
>> >>>   .../devicetree/bindings/clock/at91-clock.txt       | 34
>> >>> ++++++++++++++++++++++
>> >>>   1 file changed, 34 insertions(+)
>> >>>
>> >>> diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt
>> >>> b/Documentation/devicetree/bindings/clock/at91-clock.txt
>> >>> index 51c259a..1c46b3c 100644
>> >>> --- a/Documentation/devicetree/bindings/clock/at91-clock.txt
>> >>> +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
>> >>> @@ -90,6 +90,8 @@ Required properties:
>> >>>         "atmel,sama5d2-clk-audio-pll-pmc"
>> >>>                 at91 audio pll output on AUDIOPLLCLK that feeds the PMC
>> >>>                 and can be used by peripheral clock or generic clock
>> >>> +       "atmel,sama5d2-clk-i2s-mux":
>> >>> +               at91 I2S clock source selection
>> >>
>> >>
>> >> Is this boolean or takes some values. If latter, what are valid values?
>> >
>> >
>> > This is the compatible string of the clock driver.
>>
>> Ah, now I remember. AT91 uses fine grained clock nodes in DT. Is there
>> still a plan to fix this?
>
> I'm also interested in a plan.
>
>> >>
>> >>> +               compatible = "atmel,sama5d2-clk-i2s-mux";
>> >>> +               #address-cells = <1>;
>> >>> +               #size-cells = <0>;
>> >>
>> >>
>> >> How do you address this block? My guess is you don't because it is just
>> >> part of some other block and you are just creating this node to
>> >> instantiate a driver. Just make the node for the actual h/w block a
>> >> clock provider and define the clock ids (0 and 1).
>> >
>> >
>> > This block is not addressed, but its children are. The register we access in
>> > this driver is not part of other block. It's a SFR register, accessed
>> > through syscon and it has nothing to do with the I2S IP (see SAMA5D2 DS,
>> > page 1256, fig. 44-1: I2SC Block Diagram) that is the consumer of this
>> > clock. Adding a clock-id property in the I2S node would be just like v3 of
>> > this series, with the difference that we use clock-id instead of alias id to
>> > set the clock parent, which is not how you suggested back then.
>>
>> I wasn't suggesting a clock-id property, but a clock specifier (i.e.
>> make #clock-cells 1).
>>
>> But AT91 clocks are all a mess, so I don't know what to tell you.
>>
>
> If #clock-cells of 1 works then we should go with that. It's still weird
> that we need random nodes to add more clks, but I guess that's how it's
> going to be for each at91 clk driver until it changes to be one big
> provider node.

Seems to me that clock additions could use a new binding and we start
with a new driver that handles these few clocks initially. But I
haven't looked whether both can coexist.

Rob

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

* Re: [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock
  2018-05-31 14:20       ` Rob Herring
  2018-05-31 15:31         ` Stephen Boyd
@ 2018-05-31 16:01         ` Alexandre Belloni
  1 sibling, 0 replies; 21+ messages in thread
From: Alexandre Belloni @ 2018-05-31 16:01 UTC (permalink / raw)
  To: Rob Herring
  Cc: Codrin Ciubotariu,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	linux-kernel, devicetree, linux-clk, Linux-ALSA, Nicolas Ferre,
	Boris Brezillon, Mark Brown, Cristian.Birsan

On 31/05/2018 09:20:57-0500, Rob Herring wrote:
> On Thu, May 31, 2018 at 5:25 AM, Codrin Ciubotariu
> <codrin.ciubotariu@microchip.com> wrote:
> > On 31.05.2018 03:58, Rob Herring wrote:
> >>
> >> On Fri, May 25, 2018 at 03:34:22PM +0300, Codrin Ciubotariu wrote:
> >>>
> >>> The I2S mux clock can be used to select the I2S input clock. The
> >>> available parents are the peripheral and the generated clocks.
> >>>
> >>> Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
> >>> ---
> >>>   .../devicetree/bindings/clock/at91-clock.txt       | 34
> >>> ++++++++++++++++++++++
> >>>   1 file changed, 34 insertions(+)
> >>>
> >>> diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt
> >>> b/Documentation/devicetree/bindings/clock/at91-clock.txt
> >>> index 51c259a..1c46b3c 100644
> >>> --- a/Documentation/devicetree/bindings/clock/at91-clock.txt
> >>> +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
> >>> @@ -90,6 +90,8 @@ Required properties:
> >>>         "atmel,sama5d2-clk-audio-pll-pmc"
> >>>                 at91 audio pll output on AUDIOPLLCLK that feeds the PMC
> >>>                 and can be used by peripheral clock or generic clock
> >>> +       "atmel,sama5d2-clk-i2s-mux":
> >>> +               at91 I2S clock source selection
> >>
> >>
> >> Is this boolean or takes some values. If latter, what are valid values?
> >
> >
> > This is the compatible string of the clock driver.
> 
> Ah, now I remember. AT91 uses fine grained clock nodes in DT. Is there
> still a plan to fix this?
> 

There is still a plan to do that, hopefully soon (I'd like to aim for
the next release or the one after).

I think this one should go in as-is so it can be fixed with all the
other one at once.

-- 
Alexandre Belloni, Bootlin (formerly Free Electrons)
Embedded Linux and Kernel engineering
https://bootlin.com

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

* Re: [PATCH v4 2/7] clk: at91: add I2S clock mux driver
  2018-05-31 15:26   ` Stephen Boyd
@ 2018-06-04  8:20     ` Codrin Ciubotariu
  2018-06-12 16:33       ` Stephen Boyd
  0 siblings, 1 reply; 21+ messages in thread
From: Codrin Ciubotariu @ 2018-06-04  8:20 UTC (permalink / raw)
  To: Stephen Boyd, alexandre.belloni, alsa-devel, boris.brezillon,
	broonie, devicetree, linux-arm-kernel, linux-clk, linux-kernel,
	nicolas.ferre, robh+dt
  Cc: Cristian.Birsan

On 31.05.2018 18:26, Stephen Boyd wrote:
> Quoting Codrin Ciubotariu (2018-05-25 05:34:23)
>> This driver is a simple muxing driver that controls the
>> I2S's clock input by using syscon/regmap to change the parrent.
> 
> s/parrent/parent/

I will fix it.

> 
>> The available inputs can be Peripheral clock and Generated clock.
> 
> Why capitalize peripheral and generated?

In DS, at I2S block these clocks appear defined with capital letters... 
I will fix it.

> 
>>
>> Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
>> ---
>> diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
>> index 1254bf9..903f23c 100644
>> --- a/arch/arm/mach-at91/Kconfig
>> +++ b/arch/arm/mach-at91/Kconfig
>> @@ -27,6 +27,7 @@ config SOC_SAMA5D2
>>          select HAVE_AT91_H32MX
>>          select HAVE_AT91_GENERATED_CLK
>>          select HAVE_AT91_AUDIO_PLL
>> +       select HAVE_AT91_I2S_MUX_CLK
>>          select PINCTRL_AT91PIO4
>>          help
>>            Select this if ou are using one of Microchip's SAMA5D2 family SoC.
>> @@ -129,6 +130,9 @@ config HAVE_AT91_GENERATED_CLK
>>   config HAVE_AT91_AUDIO_PLL
>>          bool
>>   
>> +config HAVE_AT91_I2S_MUX_CLK
>> +       bool
>> +
>>   config SOC_SAM_V4_V5
>>          bool
>>   
> 
> I guess this is OK.
> 
>> diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
>> index 082596f..facc169 100644
>> --- a/drivers/clk/at91/Makefile
>> +++ b/drivers/clk/at91/Makefile
>> @@ -13,3 +13,4 @@ obj-$(CONFIG_HAVE_AT91_USB_CLK)               += clk-usb.o
>>   obj-$(CONFIG_HAVE_AT91_SMD)            += clk-smd.o
>>   obj-$(CONFIG_HAVE_AT91_H32MX)          += clk-h32mx.o
>>   obj-$(CONFIG_HAVE_AT91_GENERATED_CLK)  += clk-generated.o
>> +obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK)    += clk-i2s-mux.o
>> diff --git a/drivers/clk/at91/clk-i2s-mux.c b/drivers/clk/at91/clk-i2s-mux.c
>> new file mode 100644
>> index 0000000..2d56ded
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-i2s-mux.c
>> @@ -0,0 +1,117 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + *  Copyright (C) 2018 Microchip Technology Inc,
>> + *                     Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
>> + *
>> + *
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/of.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/regmap.h>
>> +#include <linux/slab.h>
>> +
>> +#include <soc/at91/atmel-sfr.h>
>> +
>> +#define        I2S_BUS_NR      2
>> +
>> +struct clk_i2s_mux {
>> +       struct clk_hw hw;
>> +       struct regmap *regmap;
>> +       u32 bus_id;
> 
> Can be a u8?

I think so, I will cast out_value parameter of of_property_read_u32() to u8.

> 
>> +};
>> +
>> +#define to_clk_i2s_mux(hw) container_of(hw, struct clk_i2s_mux, hw)
>> +
>> +static u8 clk_i2s_mux_get_parent(struct clk_hw *hw)
>> +{
>> +       struct clk_i2s_mux *mux = to_clk_i2s_mux(hw);
>> +       u32 val;
>> +
>> +       regmap_read(mux->regmap, AT91_SFR_I2SCLKSEL, &val);
>> +
>> +       return (val & BIT(mux->bus_id)) >> mux->bus_id;
>> +}
>> +
>> +static int clk_i2s_mux_set_parent(struct clk_hw *hw, u8 index)
>> +{
>> +       struct clk_i2s_mux *mux = to_clk_i2s_mux(hw);
>> +
>> +       return regmap_update_bits(mux->regmap, AT91_SFR_I2SCLKSEL,
>> +                                 BIT(mux->bus_id), index << mux->bus_id);
>> +}
>> +
>> +const struct clk_ops clk_i2s_mux_ops = {
> 
> static?

Yes, I will fix it.

> 
>> +       .get_parent = clk_i2s_mux_get_parent,
>> +       .set_parent = clk_i2s_mux_set_parent,
>> +       .determine_rate = __clk_mux_determine_rate,
>> +};
>> +
>> +static struct clk_hw * __init
>> +at91_clk_i2s_mux_register(struct regmap *regmap, const char *name,
>> +                         const char * const *parent_names,
>> +                         unsigned int num_parents, u32 bus_id)
>> +{
>> +       struct clk_init_data init = {};
>> +       struct clk_i2s_mux *i2s_ck;
>> +       int ret;
>> +
>> +       i2s_ck = kzalloc(sizeof(*i2s_ck), GFP_KERNEL);
>> +       if (!i2s_ck)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       init.name = name;
>> +       init.ops = &clk_i2s_mux_ops;
>> +       init.parent_names = parent_names;
>> +       init.num_parents = num_parents;
>> +       init.flags = CLK_IGNORE_UNUSED;
> 
> Really? Why?

I am thinking that there is no need to gate this clock, since there is 
no way to gate this clock in HW.

> 
>> +
>> +       i2s_ck->hw.init = &init;
>> +       i2s_ck->bus_id = bus_id;
>> +       i2s_ck->regmap = regmap;
>> +
>> +       ret = clk_hw_register(NULL, &i2s_ck->hw);
>> +       if (ret) {
>> +               kfree(i2s_ck);
>> +               return ERR_PTR(ret);
>> +       }
>> +
>> +       return &i2s_ck->hw;
>> +}

Thank you for your review. I will wait a few more days for more comments 
on this series and send a V5 afterwards.

Best regards,
Codrin

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

* Re: [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock
  2018-05-31 15:56           ` Rob Herring
@ 2018-06-07 10:30             ` Codrin Ciubotariu
  2018-06-12  8:06               ` Stephen Boyd
  0 siblings, 1 reply; 21+ messages in thread
From: Codrin Ciubotariu @ 2018-06-07 10:30 UTC (permalink / raw)
  To: Rob Herring, Stephen Boyd, Alexandre Belloni, Mark Brown
  Cc: devicetree, Linux-ALSA, linux-kernel, Boris Brezillon,
	Cristian.Birsan, linux-clk,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE

On 31.05.2018 18:56, Rob Herring wrote:
> On Thu, May 31, 2018 at 10:31 AM, Stephen Boyd <sboyd@kernel.org> wrote:
>> Quoting Rob Herring (2018-05-31 07:20:57)
>>> On Thu, May 31, 2018 at 5:25 AM, Codrin Ciubotariu
>>> <codrin.ciubotariu@microchip.com> wrote:
>>>> On 31.05.2018 03:58, Rob Herring wrote:
>>>>>
>>>>> On Fri, May 25, 2018 at 03:34:22PM +0300, Codrin Ciubotariu wrote:
>>>>>>
>>>>>> The I2S mux clock can be used to select the I2S input clock. The
>>>>>> available parents are the peripheral and the generated clocks.
>>>>>>
>>>>>> Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
>>>>>> ---
>>>>>>    .../devicetree/bindings/clock/at91-clock.txt       | 34
>>>>>> ++++++++++++++++++++++
>>>>>>    1 file changed, 34 insertions(+)
>>>>>>
>>>>>> diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt
>>>>>> b/Documentation/devicetree/bindings/clock/at91-clock.txt
>>>>>> index 51c259a..1c46b3c 100644
>>>>>> --- a/Documentation/devicetree/bindings/clock/at91-clock.txt
>>>>>> +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
>>>>>> @@ -90,6 +90,8 @@ Required properties:
>>>>>>          "atmel,sama5d2-clk-audio-pll-pmc"
>>>>>>                  at91 audio pll output on AUDIOPLLCLK that feeds the PMC
>>>>>>                  and can be used by peripheral clock or generic clock
>>>>>> +       "atmel,sama5d2-clk-i2s-mux":
>>>>>> +               at91 I2S clock source selection
>>>>>
>>>>>
>>>>> Is this boolean or takes some values. If latter, what are valid values?
>>>>
>>>>
>>>> This is the compatible string of the clock driver.
>>>
>>> Ah, now I remember. AT91 uses fine grained clock nodes in DT. Is there
>>> still a plan to fix this?
>>
>> I'm also interested in a plan.
>>
>>>>>
>>>>>> +               compatible = "atmel,sama5d2-clk-i2s-mux";
>>>>>> +               #address-cells = <1>;
>>>>>> +               #size-cells = <0>;
>>>>>
>>>>>
>>>>> How do you address this block? My guess is you don't because it is just
>>>>> part of some other block and you are just creating this node to
>>>>> instantiate a driver. Just make the node for the actual h/w block a
>>>>> clock provider and define the clock ids (0 and 1).
>>>>
>>>>
>>>> This block is not addressed, but its children are. The register we access in
>>>> this driver is not part of other block. It's a SFR register, accessed
>>>> through syscon and it has nothing to do with the I2S IP (see SAMA5D2 DS,
>>>> page 1256, fig. 44-1: I2SC Block Diagram) that is the consumer of this
>>>> clock. Adding a clock-id property in the I2S node would be just like v3 of
>>>> this series, with the difference that we use clock-id instead of alias id to
>>>> set the clock parent, which is not how you suggested back then.
>>>
>>> I wasn't suggesting a clock-id property, but a clock specifier (i.e.
>>> make #clock-cells 1).
>>>
>>> But AT91 clocks are all a mess, so I don't know what to tell you.
>>>
>>
>> If #clock-cells of 1 works then we should go with that. It's still weird
>> that we need random nodes to add more clks, but I guess that's how it's
>> going to be for each at91 clk driver until it changes to be one big
>> provider node.
> 
> Seems to me that clock additions could use a new binding and we start
> with a new driver that handles these few clocks initially. But I
> haven't looked whether both can coexist.

Mark already applied to broonie/sound.git the I2S bindings that have a 
phandle to this clock. If I am to change #clock-cells to 1, I will have 
to change the bindings to include the clock-id.
Which approach should I take now?

Thanks and best regards,
Codrin

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

* Re: [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock
  2018-06-07 10:30             ` Codrin Ciubotariu
@ 2018-06-12  8:06               ` Stephen Boyd
  0 siblings, 0 replies; 21+ messages in thread
From: Stephen Boyd @ 2018-06-12  8:06 UTC (permalink / raw)
  To: Alexandre Belloni, Codrin Ciubotariu, Mark Brown, Rob Herring
  Cc: devicetree, Linux-ALSA, linux-kernel, Boris Brezillon,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	linux-clk, Cristian.Birsan

Quoting Codrin Ciubotariu (2018-06-07 03:30:14)
> > 
> > Seems to me that clock additions could use a new binding and we start
> > with a new driver that handles these few clocks initially. But I
> > haven't looked whether both can coexist.
> 
> Mark already applied to broonie/sound.git the I2S bindings that have a 
> phandle to this clock. If I am to change #clock-cells to 1, I will have 
> to change the bindings to include the clock-id.
> Which approach should I take now?
> 

You're talking about changing the example in the binding doc? That
doesn't really matter. Consumer side of the provider has to match the
cell count from the provider so it doesn't really need to be 'fixed' in
the example at all.


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

* Re: [PATCH v4 2/7] clk: at91: add I2S clock mux driver
  2018-06-04  8:20     ` Codrin Ciubotariu
@ 2018-06-12 16:33       ` Stephen Boyd
  0 siblings, 0 replies; 21+ messages in thread
From: Stephen Boyd @ 2018-06-12 16:33 UTC (permalink / raw)
  To: Codrin Ciubotariu, alexandre.belloni, alsa-devel,
	boris.brezillon, broonie, devicetree, linux-arm-kernel,
	linux-clk, linux-kernel, nicolas.ferre, robh+dt
  Cc: Cristian.Birsan

Quoting Codrin Ciubotariu (2018-06-04 01:20:29)
> On 31.05.2018 18:26, Stephen Boyd wrote:
> > Quoting Codrin Ciubotariu (2018-05-25 05:34:23)
> > 
> >> +       .get_parent = clk_i2s_mux_get_parent,
> >> +       .set_parent = clk_i2s_mux_set_parent,
> >> +       .determine_rate = __clk_mux_determine_rate,
> >> +};
> >> +
> >> +static struct clk_hw * __init
> >> +at91_clk_i2s_mux_register(struct regmap *regmap, const char *name,
> >> +                         const char * const *parent_names,
> >> +                         unsigned int num_parents, u32 bus_id)
> >> +{
> >> +       struct clk_init_data init = {};
> >> +       struct clk_i2s_mux *i2s_ck;
> >> +       int ret;
> >> +
> >> +       i2s_ck = kzalloc(sizeof(*i2s_ck), GFP_KERNEL);
> >> +       if (!i2s_ck)
> >> +               return ERR_PTR(-ENOMEM);
> >> +
> >> +       init.name = name;
> >> +       init.ops = &clk_i2s_mux_ops;
> >> +       init.parent_names = parent_names;
> >> +       init.num_parents = num_parents;
> >> +       init.flags = CLK_IGNORE_UNUSED;
> > 
> > Really? Why?
> 
> I am thinking that there is no need to gate this clock, since there is 
> no way to gate this clock in HW.

This flag is not necessary if the clk can't be gated via hardware
control registers. Please remove the flag.


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

end of thread, other threads:[~2018-06-12 16:33 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-25 12:34 [PATCH v4 0/7] ASoC: add driver for Atmel I2S controller Codrin Ciubotariu
2018-05-25 12:34 ` [PATCH v4 1/7] dt-bindings: clk: at91: add an I2S mux clock Codrin Ciubotariu
2018-05-31  0:58   ` Rob Herring
2018-05-31 10:25     ` Codrin Ciubotariu
2018-05-31 14:20       ` Rob Herring
2018-05-31 15:31         ` Stephen Boyd
2018-05-31 15:56           ` Rob Herring
2018-06-07 10:30             ` Codrin Ciubotariu
2018-06-12  8:06               ` Stephen Boyd
2018-05-31 16:01         ` Alexandre Belloni
2018-05-25 12:34 ` [PATCH v4 2/7] clk: at91: add I2S clock mux driver Codrin Ciubotariu
2018-05-31 15:26   ` Stephen Boyd
2018-06-04  8:20     ` Codrin Ciubotariu
2018-06-12 16:33       ` Stephen Boyd
2018-05-25 12:34 ` [PATCH v4 3/7] ARM: dts: at91: sama5d2: add I2S clock muxing nodes Codrin Ciubotariu
2018-05-25 12:34 ` [PATCH v4 4/7] ASoC: atmel-i2s: dt-bindings: add DT bindings for I2S controller Codrin Ciubotariu
2018-05-29 14:59   ` Applied "ASoC: atmel-i2s: dt-bindings: add DT bindings for I2S controller" to the asoc tree Mark Brown
2018-05-25 12:34 ` [PATCH v4 5/7] ASoC: atmel-i2s: add driver for the new Atmel I2S controller Codrin Ciubotariu
2018-05-29 14:59   ` Applied "ASoC: atmel-i2s: add driver for the new Atmel I2S controller" to the asoc tree Mark Brown
2018-05-25 12:34 ` [PATCH v4 6/7] ARM: dts: at91: sama5d2: add nodes for I2S controllers Codrin Ciubotariu
2018-05-25 12:34 ` [PATCH v4 7/7] ARM: dts: at91: sama5d2 Xplained: add pin muxing for I2S Codrin Ciubotariu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).